pthread_cond_wait против семафора - PullRequest
       42

pthread_cond_wait против семафора

45 голосов
/ 16 сентября 2008

Каковы плюсы / минусы использования pthread_cond_wait или семафора? Я жду изменения состояния как это:

pthread_mutex_lock(&cam->video_lock);
while(cam->status == WAIT_DISPLAY) {
    pthread_cond_wait(&cam->video_cond, &cam->video_lock);
}
pthread_mutex_unlock(&cam->video_lock);

Используя правильно инициализированный семафор, думаю, я мог бы сделать это так:

while(cam->status == WAIT_DISPLAY) {
    sem_wait(&some_semaphore);
}

Каковы плюсы и минусы каждого метода?

Ответы [ 4 ]

61 голосов
/ 20 сентября 2008

Семафор идеально подходит для модели «производитель-потребитель», хотя у него есть и другие применения. Ваша логика программы отвечает за то, чтобы количество ожидаемых сообщений составлялось правильно. Если вы публикуете семафор, и никто еще не ждет его, тогда, когда они ждут, они сразу же продолжаются. Если ваша проблема такова, что ее можно объяснить с помощью значения счетчика семафора, то ее легко решить с помощью семафора.

Переменная условия в некоторых отношениях немного более прощающая. Например, вы можете использовать cond_broadcast, чтобы разбудить всех официантов, при этом производитель не знает, сколько их. И если вы cond_signal condvar, на котором никто не ждет, то ничего не произойдет. Это хорошо, если вы не знаете, будет ли заинтересованный слушатель. Именно поэтому слушатель должен всегда проверять состояние с мьютексом, удерживаемым перед ожиданием - если он этого не делает, он может пропустить сигнал и не просыпаться до следующего (что может быть никогда).

Таким образом, условная переменная подходит для уведомления заинтересованных сторон об изменении состояния: вы получаете мьютекс, изменяете состояние, сигнализируете (или транслируете) condvar и освобождаете мьютекс. Если это описывает вашу проблему, вы находитесь на территории Кондвар. Если разные слушатели интересуются разными состояниями, вы можете просто транслировать, и каждый из них по очереди проснется, выяснит, нашли ли они желаемое состояние, и если нет, то подождут снова.

Очень мрачно пытаться делать подобные вещи с мьютексом и семафором. Проблема возникает, когда вы хотите взять мьютекс, проверить состояние, а затем дождаться изменений на семафоре. Если вы не можете атомарно освободить мьютекс и ждать на семафоре (чего нельзя сделать в pthreads), вы в конечном итоге ждете семафор, удерживая мьютекс. Это блокирует мьютекс, а это означает, что другие не могут принять изменения, которые вас волнуют. Таким образом, у вас будет соблазн добавить еще один мьютекс, который зависит от ваших конкретных требований. И, может быть, еще один семафор. В результате получается неправильный код с опасными условиями гонки.

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

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

19 голосов
/ 16 сентября 2008

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

Например, предположим, что у вас есть код, который требует мьютекс, называемый m. Однако ему нужно подождать, пока какой-то другой поток завершит свою задачу, поэтому он ожидает семафор с именем s. Теперь любой поток, которому требуется m, заблокирован для запуска, даже если поток с m ожидает s. Подобные ситуации могут быть разрешены с помощью условных выражений. Когда вы ожидаете условия, мьютекс, удерживаемый в данный момент, освобождается, поэтому другие потоки могут получить мьютекс. Итак, вернемся к нашему примеру и предположим, что вместо s было использовано условное c. Наш поток теперь получает m, а затем условное ожидание c. Это освобождает m, чтобы другие потоки могли продолжить. Когда c становится доступным, m повторно запрашивается, и наш оригинальный поток может продолжать весело продолжать свой путь.

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

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

5 голосов
/ 12 января 2009

2-ой фрагмент - не совсем.

В других ответах есть хорошее обсуждение относительных достоинств; Я просто добавлю, что pthread_cond_broadcast - явное преимущество условных переменных.

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

Действительно, во втором фрагменте у вас нет блокировки, защищающей чтение статуса cam->, поэтому доступ к нему осуществляется через гонку данных. Большинство платформ позволят вам справиться с этим в этом конкретном примере, но с неопределенной семантикой, в POSIX и в модели памяти следующих стандартов C / C ++.

Фактически, реальное состояние гонки возможно, если другой поток выделяет новую структуру кулачка и перезаписывает кулачок; ожидающий поток может увидеть обновление указателя 'cam', не видя инициализации cam-> status. Действительно, второй фрагмент кода вызывает проблемы, в данном случае и в целом.

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/

0 голосов
/ 16 сентября 2008

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

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

В противном случае, если состояние является сложным, и разные части кода ожидают различных условий одной и той же переменной (например, здесь вы хотите, чтобы x <10; там вы хотели бы y> x), используйте cond_wait.

...