о синхронизации с использованием нескольких семафоров

Привет, я работаю над заданием об использовании потоков POSIX с несколькими семафорами. краткое объяснение назначения: есть 4 различных пакета данных (символ/видео/аудио/изображение), каждый из которых передается отдельным потоком, а также у нас есть общий буфер. максимальное количество потоков, которые могут работать в системе, будет поддерживаться пользователем в качестве входных данных. Например; если пользователь вводит 10, то может быть создано максимум 10 потоков для передачи пакетов данных через буфер в заданное время. теперь меня смущает то, что этот буфер может мгновенно содержать ограниченное количество пакетов. (например, он может содержать максимум 10 пакетов символов и 20 видеопакетов и т. д.), поэтому у нас должны быть разные семафоры для каждого типа данных. проблема, которую я знаю, как контролировать размер буфера с помощью семафора, который очень прост, но не могу установить правильную идею использования семафоров пакетов». даже я пробовал несколько разных методов, я всегда сталкивался с ошибками взаимоблокировки. вот мой псевдокод, чтобы лучше понять мою программу.

define struct packege
define semaphore list

main

initialize variables and semaphores

while threadCounter is less than MaxThreadNumber

switch(random)
case 0: create a character package
    create a thread to insert the package in buffer
case 1: create a video package
    create a thread to insert the package in buffer
case 2: create an image package
    create a thread to insert the package in buffer
case 3: create an audio package
    create a thread to insert the package in buffer

increment  threadCounter by one
end of while

create only one thread which will make the dequeue operation
end of main

producer function

for i->0 to size_of_package
    sem_wait(empty_buffer) // decrement empty_buffer semaphore by size of package

    lock_mutex
        insert item into queueu
        decrement counter of the buffer by size of package
    unlock_mutex

for i->0 to size_of_package
    sem_post(full_buffer) // increment full_buffer semaphore by size of package

end of producer function

consumer function

while TRUE // Loops forever

    lock_mutex

        if queue is not empty
            dequeue

        increment counter of the buffer size of package

    unlock_mutex

for i->0 to size_of_package // The reason why i making the sem_wait operation here is i cant make the dequeue in outer region of mutex.
    sem_wait(full_buffer)
for i->0 to size_of_package
    sem_post(empty_buffer)
end of consumer function

с этой реализацией программа работает корректно. но я не мог правильно использовать семафоры, принадлежащие потокам пакетов. Я могу выслушать каждую рекомендацию и буду признателен за каждый ответ.


person quartaela    schedule 29.10.2012    source источник
comment
В вашем коде потребитель фактически не знает, какие данные он считывает из буфера. Это нормально?   -  person didierc    schedule 29.10.2012
comment
Думаю, вы хотите указать, что хотите использовать один семафор для нормирования общего количества одновременных потоков и N-семафоров больше, по одному для каждого пакета типа для нормирования количества одновременные потоки для любого отдельного типа пакета. Это хоть близко к точности? Немного запутался..   -  person WhozCraig    schedule 29.10.2012
comment
@didierc да, нет проблем, я упорядочил параметры, чтобы они работали правильно. прежде всего, я отправляю структуру в качестве параметра производителю, а потребитель выполняет операцию удаления из очереди во мьютексе. И очередь определяется глобально.   -  person quartaela    schedule 29.10.2012


Ответы (2)


Это не то, как используются семафоры. Управляющие переменные/структуры буфера должны подсчитывать, сколько сообщений содержится в буфере и каких типов. Мьютекс защищает буфер и его управляющие переменные/структуры от одновременного доступа со стороны разных потоков. Семафор, если он используется, просто сигнализирует потребителю о состоянии буфера и не имеет никакого отношения к размерам пакетов; он, конечно, не увеличивается на размер пакета!

Вам было бы лучше использовать переменные условия pthread вместо семафоров. Они используются в сочетании с мьютексом pthread, чтобы гарантировать передачу сигналов между потоками без состязаний. Цикл производителя делает это:

  • блокирует мьютекс,
  • изменяет буфер и т. д., чтобы добавить новые пакеты,
  • сигнализирует условную переменную, и
  • разблокирует мьютекс.

Потребительский цикл делает это:

  • блокирует мьютекс,
  • обрабатывает все буферизованные данные,
  • ожидает переменную условия.

Прочитайте pthread_cond_init, pthread_cond_signal и pthread_cond_wait.

person William Morris    schedule 29.10.2012
comment
на самом деле я знаю, как использовать условные переменные, однако в задании, которое они хотели, просто использовали наши семафоры. вот так For the sake of example, the buffer can contain 15 multimedia video packets at maximum, meaning that the sixteenth MM-Video packet has to be blocked by a semaphore utilization. - person quartaela; 29.10.2012

Поскольку это задание, вам, вероятно, не нужно читать и записывать данные реальных пакетов, а просто имитировать их обработку.

В этом случае проблема сводится к тому, как эффективно блокировать потоки-производители, когда они достигают предела пакетов, которые они могут записать в буфер. На данный момент вы используете семафор для подсчета отдельных элементов пакета, записанных в буфер, насколько я понимаю.

Представьте, что ваши записи в буфер являются атомарными, и вы просто хотите считать пакеты, а не элементы пакета. Каждый раз, когда производитель записывает пакет, он должен сигнализировать об этом потребителю с помощью соответствующего семафора, и каждый раз, когда потребитель читает пакет, он должен сигнализировать об этом соответствующему производителю.

Выделю еще несколько моментов:

  • Важным свойством семафора является то, что он блокируется, когда достигает нуля. Например, если его начальное значение равно 10, после 10 последовательных sem_get блокируется 11-й.
  • У вас есть 4 типа пакетов, каждый с разным порогом количества, которое может быть записано в буфер.

Как я уже сказал, производитель должен сообщить, что он написал пакет, но он также должен быть остановлен, как только он достигнет порога. Чтобы добиться этого, вы заставляете его получать семафор каждый раз, когда он отправляет новый пакет, с sem_get. И у вас есть потребитель, выполняющий sem_post каждый раз, когда он читает пакет, обратное тому, что вы делали с вашей версией с одним семафором. Однако, поскольку вы хотите, чтобы производитель остановился на пороге, вы инициализируете семафор емкостью N - 1, где N является порогом. Обратите внимание, что вы должны сигнализировать о доступности нового пакета после того, как вы записали его в буфер, иначе потребитель может заблокировать буфер.

producer<type> function

  write_packet()  // put the packet in the buffer
  sem_wait(type)    // signal a new packet is available 
  // (if there's not enough space for another packet, the producer will block here) 

end producer<type> function

consumer function

  while TRUE // Loops forever

      switch packet_available() // look if there's a new packet available
       case video:
         read_packet<video>()
         sem_post(video)
       (...)
       default: // no packet available, just wait a little
          sleep()
      end if
  end while

Вам все еще нужно определить функции packet_read, packet_write и packet_available, возможно, используя мьютекс для ограничения доступа к буферу.

person didierc    schedule 29.10.2012
comment
да, ваше понимание верно. запутанная часть. например, предположим, что я создал данные изображения. чем я должен использовать sem_wait(&empty_image) и sem_post(&full_image) в case 2: section или в producer function_? С другой стороны, правильно ли, что у нас есть 4 диф. типы, поэтому операции потребительского семафора будут зависеть от операторов if-else. например, if(datatype==1) sem_wait(&full_video) else if(datatype==2) sem_wait(&empty_image) и т. д. - person quartaela; 29.10.2012
comment
@quartaela: что такое empty_image и full_image? Зачем использовать 2 разных семафора для пакетов изображений? - person didierc; 29.10.2012
comment
empty_image инициализирует максимальное количество данных изображения, которое может храниться в буфере, а full_image инициализируется 0, что означает полную часть буфера. поэтому For the sake of example, the buffer can contain 15 multimedia video packets at maximum, meaning that the sixteenth MM-Video packet has to be blocked by a semaphore utilization. поэтому всякий раз, когда передается видеопакет, я сообщу производителю, чтобы он добавил последний. поэтому я определяю 4 пустых и 4 полных переменных семафора для каждого типа данных. разве это не правильный путь _? - person quartaela; 29.10.2012
comment
Это требование вашей задачи? Если нет, просто используйте 4 семафора, по одному на тип пакета, и все станет проще. - person didierc; 29.10.2012
comment
да, это требование назначения. ну, это запутанная часть, как я уже сказал. :). тем не менее, я могу не делать эту часть, она просто усложняется - person quartaela; 29.10.2012
comment
Это кажется немного странным. Нет необходимости в 2 семафорах для каждого типа пакета. Каждый производитель должен использовать один семафор, чтобы указать, что он написал пакет. А потребитель должен проверять каждый семафор и указывать, когда он прочитал пакет данного типа. - person didierc; 29.10.2012
comment
на самом деле нам не нужно использовать 2 семафора для каждого типа пакета. это было мое предположение. это просто указывает, что мы должны использовать семафоры для каждого пакета. поэтому, насколько я понимаю, я думал, что должен использовать 2 семафора для каждого типа пакета. Иначе как мне справиться с ограничением на количество пакетов в буфере. - person quartaela; 29.10.2012