Не блокирует мьютекс для pthread_cond_timedwait и pthread_cond_signal (в Linux) - PullRequest
6 голосов
/ 16 июня 2009

Есть ли какой-либо недостаток в вызове pthread_cond_timedwait без предварительной блокировки ассоциированного мьютекса, а также без блокировки мьютекса при вызове pthread_cond_signal?

В моем случае нет условий для проверки, я хочу поведение, очень похожее на Java wait (long) и notify ().

Согласно документации, может быть «непредсказуемое поведение при планировании». Я не уверен, что это значит.

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

Ответы [ 6 ]

11 голосов
/ 16 июня 2009

Первый не в порядке:

pthread_cond_timedwait() и pthread_cond_wait() функции должны блок на условной переменной. Oни должен вызываться с мьютексом, заблокированным вызывающая нить или неопределенная результаты поведения.

http://opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html

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

Второй тревожит:

если прогнозируемое поведение планирования требуется, то этот мьютекс заблокирован вызывающая нить pthread_cond_signal() или pthread_cond_broadcast().

http://www.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_signal.html

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

Как правило, однако, с помощью переменной условия вы хотите установить условие (по крайней мере, флаг) и сигнал, а не просто сигнал, и для этого вам нужно взять мьютекс. Причина в том, что в противном случае, если вы работаете одновременно с другим потоком, вызывающим wait (), вы получаете совершенно другое поведение в зависимости от того, выиграют ли wait () или signal (): если в первый раз подойдет signal (), то вы дождитесь полного таймаута, даже если сигнал, который вас волнует, уже произошел. Это редко то, что хотят пользователи условных переменных, но может подойти вам. Возможно, именно это и подразумевают в документах «непредсказуемое поведение планировщика» - внезапно временной интервал становится критическим для поведения вашей программы.

Кстати, в Java для блокировки notify () или notifyAll () необходимо иметь блокировку:

Этот метод должен вызываться только Нить, которая является владельцем этого монитор объекта.

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#notify()

Синхронизированное поведение Java {/} / wait / notifty / notifyAll аналогично pthread_mutex_lock / pthread_mutex_unlock / pthread_cond_wait / pthread_cond_signal / pthread_cond_broadcast, и не случайно.

9 голосов
/ 19 февраля 2010

Прекрасное "Программирование с помощью потоков POSIX" Бутенхофа обсуждает это прямо в конце главы 3.3.3.

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

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

3 голосов
/ 16 июня 2009

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

1 голос
/ 16 июня 2009

Я думаю, что это должно работать (обратите внимание на непроверенный код):

// initialize a semaphore
sem_t sem;
sem_init(&sem,
    0, // not shared
    0  // initial value of 0
    );


// thread A
struct timespec tm;
struct timeb    tp;

const long sec      = msecs / 1000;
const long millisec = msecs % 1000;

ftime(&tp);
tp.time += sec;
tp.millitm += millisec;
if(tp.millitm > 999) {
    tp.millitm -= 1000;
    tp.time++;
}
tm.tv_sec  = tp.time;
tm.tv_nsec = tp.millitm * 1000000;

// wait until timeout or woken up
errno = 0;
while((sem_timedwait(&sem, &tm)) == -1 && errno == EINTR) {
    continue;
}

return errno == ETIMEDOUT; // returns true if a timeout occured


// thread B
sem_post(&sem); // wake up Thread A early
0 голосов
/ 29 мая 2013

Условия должны сигнализироваться за пределами мьютекса, когда это возможно. Мьютексы - необходимое зло в параллельном программировании. Их использование приводит к конфликту, который лишает систему максимальной производительности, которую она может получить от использования нескольких процессоров.

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

Примечания о "предсказуемом поведении планировщика" в стандарте полностью поддельные.

Когда мы хотим, чтобы машина выполняла операторы в предсказуемом, четко определенном порядке, инструментом для этого является последовательность операторов в одном потоке выполнения: S1 ; S2. Оператор S1 «запланирован» до S2.

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

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

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

0 голосов
/ 17 июня 2009

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

В принципе, если какие-либо документы предполагают, что может произойти что-то неопределенное / непредсказуемое, если вы не будете делать то, что вам говорят в документах, вам лучше это сделать. Еще вещи могут взорваться на вашем лице. (И он не взорвется, пока вы не введете код в производство, просто чтобы раздражать вас еще больше. По крайней мере, это мой опыт) * ​​1003 *

...