Не блокирует мьютекс для pthread_cond_timedwait и pthread_cond_signal (в Linux)

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

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

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

Кажется, что пример программы работает нормально, не блокируя мьютексы.

 Evan Teran16 июн. 2009 г., 20:36
просто чтобы быть ясно, что вы хотите, чтобы ждать до N секунд, если вы не проснулись рано?
 Sid Datta16 июн. 2009 г., 21:26
да только это. Возможно, семафоры - лучшая сделка.

Ответы на вопрос(6)

Решение Вопроса

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

The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. They shall be called with mutex locked by the calling thread or undefined behavior results.

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

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

Второе беспокоит:

if predictable scheduling behaviour is required, then that mutex is locked by the thread calling pthread_cond_signal() or pthread_cond_broadcast().

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

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

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

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

This method should only be called by a thread that is the owner of this object's monitor.

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, и не случайно.

 16 июн. 2009 г., 22:39
Java выдает, если монитор не принадлежит нужному потоку.
 16 июн. 2009 г., 21:47
Возможно, вы захотите исправить ссылку на JavaDoc ... скобки должны быть частью URL.
 16 июн. 2009 г., 23:32
Я думаю, что все это означает, что когда вы изменяете условие, затем освобождаете блокировку, другой поток (назовите его B) может захватить мьютекс, проверить условие и увидеть, что он сигнализирован, и приступить к нему. Тем временем cond_signal () запускается и другой поток (назовите его A), который собирался проверить блоки условий, пытаясь заблокировать мьютекс. Когда A наконец захватывает мьютекс, он видит, что B уже сбросил условие, и снова возвращается в спящий режим в cond_wait. Если все потоки "равны" это безвредно, но в теории нить А может стать голодной.

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

// 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
 16 июн. 2009 г., 21:18
+1, хотя будьте осторожны, что sem_timed_wait не является обязательным. Под этим я подразумеваю более необязательный, чем семафоры, которые сами по себе также являются необязательными, но нет особого смысла иметь pthreads без них ...

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

Превосходное программирование Butenhof'а с использованием потоков POSIX & quot; обсуждается это прямо в конце главы 3.3.3.

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

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

 01 июн. 2013 г., 19:34
@Kaz: да, вы удаляете машинные циклы из блокировки и оптимизируете на микроуровне, но если у вас тогда получится инверсия приоритетов, и ваш высокоприоритетный поток ожидает в потоках с более низким приоритетом, вероятно, у вас есть подчиненный оптимальная программа.
 08 авг. 2011 г., 03:03
+1 для хорошего объяснения неуловимого "почему я должен заблокировать мьютекс для сигнализации условий var?" Вопрос!
 29 мая 2013 г., 18:37
Сигнализация condvar без блокировки мьютекса - это не просто потенциальная оптимизация. Вы вынимаете сотни ненужных машинных циклов из замка.

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

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

 17 июн. 2009 г., 15:03
Я не согласен. Смысл «непредсказуемого поведения планировщика» является неоднозначным, но я вполне уверен, что намерение не подразумевать «неопределенное поведение», или они сказали бы это, как они это делают в pthread_cond_wait.

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

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

Заметки о «предсказуемом поведении планировщика» в стандарте полностью фальшивые.

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

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

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

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

Ваш ответ на вопрос