я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим?
дедлок?
если дедлок то что это за фуфло такое? на чистом винапи можно легко избежать этой проблемы, потому что там если эвент устанорвлен, то Wait не будет ждать.
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Здравствуйте, Barbar1an, Вы писали:
B>я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим? B>дедлок?
B>если дедлок то что это за фуфло такое? на чистом винапи можно легко избежать этой проблемы, потому что там если эвент устанорвлен, то Wait не будет ждать.
То, что евент установлен, за это отвечает состояние этого объекта — некий bool, условно говоря.
С условной переменной функции ожидания проверяют предикат. Если там проверяется какой-то bool, то будет как с виндовым эвентом.
wait увидит true и не будет ждать. notify_one/notify_all только выполняют пробуждение ждущих потоков, чтобы те смогли проверить свои предикаты.
В условной переменной можно использовать и более сложные состояния, опрос которых при вызове предиката защищен мьютексом.
Например, аналог WaitForMultipleObjects можно сделать из условной переменной и массива bool.
Здравствуйте, Barbar1an, Вы писали:
B>я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим? B>дедлок?
Дедлок это по определению другая ситуация.
А так да, до следующего вызова "notify" "wait" не вернет управление.
Но не понятно в чем проблема,
все же защищено mutex и поэтому атомарно.
То есть
То есть если даже notify_all будет вызван до wait,
ничего плохого не произойдет и wait сразу вернет управление
ничего не блокируя благодаря предикату в wait
Здравствуйте, qaz77, Вы писали:
Q>Здравствуйте, Barbar1an, Вы писали:
B>>я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим? B>>дедлок?
B>>если дедлок то что это за фуфло такое? на чистом винапи можно легко избежать этой проблемы, потому что там если эвент устанорвлен, то Wait не будет ждать.
Q>То, что евент установлен, за это отвечает состояние этого объекта — некий bool, условно говоря. Q>С условной переменной функции ожидания проверяют предикат. Если там проверяется какой-то bool, то будет как с виндовым эвентом. Q>wait увидит true и не будет ждать. notify_one/notify_all только выполняют пробуждение ждущих потоков, чтобы те смогли проверить свои предикаты.
Q>В условной переменной можно использовать и более сложные состояния, опрос которых при вызове предиката защищен мьютексом. Q>Например, аналог WaitForMultipleObjects можно сделать из условной переменной и массива bool.
да , но меня напрягает что для тривиальной задачи "подождать пока чтото закончится", мне нужно иметь ТРИ переменые: мутекс, кондишн и чтото для предиката
хотя это можно сделать одним винапишным Event'ом
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
B>да , но меня напрягает что для тривиальной задачи "подождать пока чтото закончится", мне нужно иметь ТРИ переменые: мутекс, кондишн и чтото для предиката B>хотя это можно сделать одним винапишным Event'ом
Есть разные подходы к обработке событий, которые имеют разную производительность и подходят к различным ситуациям:
edge-triggered
level-triggered
Условные переменные — это edge, а event в winapi — level. Неудивительно, что возникают неудобства, если использовать один подход для эмуляции другого
Собственно, если хочешь использовать только чистый с++, то возьми класс, который реализует примитив Event и скрывает всю работу с условными переменными внутри себя. А потом используй только этот класс.
B>хотя это можно сделать одним винапишным Event'ом
А что не std::future, кстати говоря?
Здравствуйте, Barbar1an, Вы писали:
B>да , но меня напрягает что для тривиальной задачи "подождать пока чтото закончится", мне нужно иметь ТРИ переменые: мутекс, кондишн и чтото для предиката B>хотя это можно сделать одним винапишным Event'ом
Просто более низкоуровневая абстракция, на базе которой можно сделать и семафор, и много чего еще.
Если хочется как виндовый эвент и одной переменной, сделай класс типа этого:
Здравствуйте, watchmaker, Вы писали:
W>А что не std::future, кстати говоря?
поток который я жду создается не мной
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Q>Просто более низкоуровневая абстракция, на базе которой можно сделать и семафор, и много чего еще.
Ну-у можно сделать condition_variable из семафора, а можно наоборот.
Наоборот, кстати, лучше выходит, оптимальнее, что говорит о том, что не очень низкоуровневая абстракция.
Здравствуйте, Alexander G, Вы писали:
AG>Ну-у можно сделать condition_variable из семафора, а можно наоборот. AG>Наоборот, кстати, лучше выходит, оптимальнее, что говорит о том, что не очень низкоуровневая абстракция.
В контексте поддержки многопоточности в С++11 есть только мьютекс и условная переменная.
Если оставаться в рамках std::, то семафор придется из них собирать...
А так, согласен, что на разных платформах те или иные примитивы более родные и/или эффективные.
Здравствуйте, qaz77, Вы писали: Q>Если хочется как виндовый эвент и одной переменной, сделай класс типа этого:
Вот конкретно этот thread_event содержит потенциальную ошибку:
thread 1 | thread 2
|
if ( thread_event::get() ) |
| thread_event::signal();
thread_event::reset(); |
thread_event::wait(); <--- signal потерен |
Методы thread_event::get() и thread_event::reset() следует выкинуть, как непригодные к использованию.
BFE>Методы thread_event::get() и thread_event::reset() следует выкинуть, как непригодные к использованию. BFE>
Здесь get скорее диагностический метод, типа WaitForSingleObject(hEvent, 0).
А reset — это ручной сброс, аналог ResetEvent из Win API.
О какой-то потере здесь говорить бессмысленно, все зависит от контекста использования.
Люди привыкшие писать под винду на эвентах знают, как все это приготовить.
Q>В контексте поддержки многопоточности в С++11 есть только мьютекс и условная переменная. Q>Если оставаться в рамках std::, то семафор придется из них собирать...
Это да. Хотя контексте C++20 дали и семафор, и барьер, и atomic<T>::wait, дождаться теперь его массовости.
Q>А так, согласен, что на разных платформах те или иные примитивы более родные и/или эффективные.
Даже от версии.
В Windows XP, то мьютекс на событие/семафор -- это самое "родное", за неимением ничего другого (ну кроме рекрусивного юзерспейс мьютекса CRITICAL_SECTION).
С Vista есть уже SRWLOCK / CONDITION_VARIABLE, и из-за отсуствия проблем с инициализацией, нехваткой ресурсов, и вообще эффективным user-ожиданием, уже есть причины предпочесть их, несмотря на то, что показанный выше семафор на них сложноват и совершает лишние операции. Т.е. на Windows Event как бы можно сэкономить на спичках, но при этом придётся самомоу городить атомарную юзерспейс часть.
Наконец, WaitOnAddress с Windows 8 имеет и плюсы с эффективной иницииализацией, и большую свободу действий, так, что семафор, или что там на нём строить решили, не будет делать ничего лишнего.
Здравствуйте, qaz77, Вы писали:
Q>Здесь get скорее диагностический метод, типа WaitForSingleObject(hEvent, 0).
Такая диагностика скорее введёт в заблуждение, чем поможет.
Q>А reset — это ручной сброс, аналог ResetEvent из Win API.
А кроме "ручного сброса" другого и нет в коде.
Q>О какой-то потере здесь говорить бессмысленно, все зависит от контекста использования.
В большинстве случаев эту ошибку не заменят, просто в редких случаях (очень редких) программа будет подвисать на какое-то (timeout) время или до второго события...
Q>Люди привыкшие писать под винду на эвентах знают, как все это приготовить.
Если я правильно помню, на виндах WaitFor... сбрасывает эвент в несигнальное состояние. В подавляющем большинстве случаев это важно для корректной работы приложения.
Здравствуйте, B0FEE664, Вы писали:
Q>>Люди привыкшие писать под винду на эвентах знают, как все это приготовить. BFE>Если я правильно помню, на виндах WaitFor... сбрасывает эвент в несигнальное состояние. В подавляющем большинстве случаев это важно для корректной работы приложения.
Есть auto-reset event, который сбрасывается сам, он работает как бинарный семафор,
А есть manual-reset, который не сбрасывается сам ("бесконечноарный семафор" )
Здравствуйте, B0FEE664, Вы писали:
BFE>Если я правильно помню, на виндах WaitFor... сбрасывает эвент в несигнальное состояние. В подавляющем большинстве случаев это важно для корректной работы приложения.
Там есть два вида событий — с ручным сбросом и с автоматическим.
При ручном сбросе wait не меняет состояния, т.е. при вызове signal проснутся все ожидающие потоки.
Если какие-то потоки не дошли еще до wait в момент вызова signal, то такие потоки увидят то состояние, которое будет при входе в wait
(это та ситуация, которая вызывала сомнения в исходном вопросе).
Обычно reset и signal вызывает один поток, который выполняет какую-то работу, а wait — другие потоки, которым нужен результат этой работы.
Например, такая иллюстрация.
Индексирующий какой-то текст поток управляет событием, а потоки выполняющие поиск ждут готовности индекса.
index thread | search thread 1 | search thread 2
-----------------------------|----------------------------|--------------------------------
ev.reset() // no valid index | |
| ev.wait() // not ready yet |
// build full text index... | |
| |
ev.signal() // ready | // wake up |
| | ev.wait() // pass through
Тут событие используется как флаг валидности индекса.
В общем, событие с ручным сбросом — это почти как std::atomic<bool>, но с возможностью ожидания.
Здравствуйте, Barbar1an, Вы писали:
B>я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим? B>дедлок?
B>если дедлок то что это за фуфло такое? на чистом винапи можно легко избежать этой проблемы, потому что там если эвент устанорвлен, то Wait не будет ждать.
Многих сбивает с толку, что в С++ condition variables являются всего лишь реализацией давно известного «монитора».
Здравствуйте, B0FEE664, Вы писали:
Q>>Здесь get скорее диагностический метод, типа WaitForSingleObject(hEvent, 0). BFE>Такая диагностика скорее введёт в заблуждение, чем поможет.
Не согласен.
Могут быть такие места в программе, где точно известно о требуемом состоянии event'ов.
Почему бы не поставить там assert?
Я имею в виду поток, который рулит событиями, т.е. никакой гонки нет в принципе.
Например, есть три события, состояния которых связаны инвариантом.
Поток, который ими рулит, меняет состояния событий (reset/signal) в критической секции для атомарности с т.з. других потоков.
Как проверить инвариант внутри критической секции?
Например, так:
assert(ev1.get() && ev2.get() && !ev3.get());
Вот такой еще пример.
В posix для семафора есть функция sem_getvalue.
Там в любой момент может значение семафора изменится, но тем не менее функция такая есть.
Здравствуйте, Barbar1an, Вы писали:
B>я так и не понял из доков, если notify_all вызван до того как ожидатель вызвал wait , то что мы получим? B>дедлок?
Получим то, что ожидатель не будет нотифицирован. А дедлок это или не дедлок, зависит от того, как условная переменная используется.
B>если дедлок то что это за фуфло такое? на чистом винапи можно легко избежать этой проблемы, потому что там если эвент устанорвлен, то Wait не будет ждать.
Именно поэтому условные переменные используются в связке с мьютексом.