#c #linux #pthreads #signals
#c #linux #pthreads #сигналы
Вопрос:
Допустим, у вас есть этот код
pthread_mutex_lock(amp;cam->video_lock);
while(cam->status == WAIT_DISPLAY) // <-- Why is this a 'while' and not an 'if'?
pthread_cond_wait(amp;cam->video_cond, amp;cam->video_lock);
pthread_mutex_unlock(amp;cam->video_lock);
Мой вопрос в том, зачем вам здесь нужен цикл while. Разве pthread_cond_wait не будет просто ждать, пока поток сигнализации не подаст сигнал cam_video_cond? Хорошо, я знаю, что у вас может быть случай, когда cam->status не равно WAIT_DISPAY при вызове pthread_cond_wait , но в этом случае вы могли бы просто проверить это с помощью условия if, а не использовать while .
Я что-то здесь упускаю? Мое понимание pthread_cond_wait заключается в том, что он просто ждет бесконечно, если cam_video_cond не сигнализируется. Более того, он разблокирует мьютекс cam_video_lock при вызове, но когда условие сигнализируется, перед возвратом он повторно блокирует cam_video_lock . Я прав?
Комментарии:
1. Тот же вопрос здесь, unix.com/programming/149791-condition-variables.html
Ответ №1:
Рекомендуется, чтобы все потоки проверяли условие после возврата из pthread_cond_wait, потому что есть несколько причин, по которым условие может быть неверным. Одной из этих причин является ложное пробуждение; то есть поток может быть разбужен, даже если ни один поток не сигнализировал о состоянии.
Источник: ложное пробуждение
Ответ №2:
Ложные пробуждения — это одна причина, но законные, но посторонние пробуждения — другая.
Рассмотрим:
-
Вы помещаете задание в очередь.
-
Вы сигнализируете о переменной условия, пробуждая поток A.
-
Вы помещаете задание в очередь.
-
Вы сигнализируете о переменной условия, пробуждая поток B.
-
Поток A становится запланированным, выполняет первое задание.
-
Поток A находит очередь непустой и выполняет второе задание.
-
Поток B запускается по расписанию после пробуждения, но обнаруживает, что очередь все еще пуста.
Ответ №3:
По соображениям производительности POSIX API позволяет ОС пробуждать ваш поток, даже если условие не было выполнено (это называется ложным пробуждением).
Ответ №4:
Причина необходимости использования цикла while заключается в том, что в std::condition_variable
для пробуждения ожидающих потоков есть только две функции:
notify_one()
который пробуждает один из потоков, ожидающих в данный момент этого условия, без возможности указать, какой именно из них должен быть активирован.notify_all()
который пробуждает все потоки, ожидающие этого условия.
И поскольку std::condition_variable имеет только две функции для пробуждения ожидающих потоков, вы либо разбудите один случайный поток с помощью notify_one()
, либо разбудите все ожидающие потоки с помощью notify_all()
. Очевидно, что это не удовлетворяет, и нам нужно как-то указать, какой поток должен быть активирован. И в этой ситуации эта семантика может помочь:
while (!ready) cv.wait(lck);
оставшаяся часть кода
здесь ready
— результат предиката, который вернет true только тогда, когда выполняется конкретное условие, которое вам нужно.
Переменные условия используются для ожидания, пока определенный предикат условия не станет истинным. Этот предикат условия устанавливается другим потоком, обычно тем, который сигнализирует условие.
Семантика ожидания условия
Предикат условия должен быть защищен мьютексом. При ожидании условия подпрограмма ожидания (либо подпрограмма pthread_cond_wait, либо подпрограмма pthread_cond_timedwait) атомарно разблокирует мьютекс и блокирует поток. Когда условие сигнализируется, мьютекс повторно блокируется, и возвращается подпрограмма ожидания. Важно отметить, что когда подпрограмма возвращается без ошибок, предикат все еще может быть false .
Причина в том, что может быть разбужено более одного потока: либо поток, вызываемый подпрограммой pthread_cond_broadcast, либо неизбежная гонка между двумя процессорами одновременно разбудила два потока. Первый поток, блокирующий мьютекс, заблокирует все остальные пробужденные потоки в подпрограмме ожидания, пока мьютекс не будет разблокирован программой. Таким образом, предикат мог измениться, когда второй поток получает мьютекс и возвращается из подпрограммы ожидания.
В общем, всякий раз, когда возвращается ожидание условия, поток должен повторно оценить предикат, чтобы определить, может ли он безопасно продолжить, должен снова ждать или должен объявить тайм-аут. Возврат из подпрограммы ожидания не подразумевает, что предикат имеет значение true или false . Рекомендуется, чтобы ожидание условия было заключено в «цикл while», который проверяет предикат.
Дополнительно: в зависимости от сложности создания условий пробуждения, которые могут быть непредсказуемыми в многопоточных системах, потоки могут просыпаться в любое время по любой причине. Это называется ложным пробуждением. Решение: используйте цикл, чтобы всегда проверять переменную условия.
Комментарии:
1.
In general, whenever a condition wait returns, the thread should reevaluate the predicate to determine whether it can safely proceed, should wait again, or should declare a timeout. A return from the wait subroutine does not imply that the predicate is either true or false. It is recommended that a condition wait be enclosed in a "while loop" that checks the predicate.
должно быть выделено