Информация об изменениях

Сообщение Re[2]: C++11: Синхронизация - Условные переменные и ложные п от 03.04.2015 10:10

Изменено 03.04.2015 10:56 uzhas

Здравствуйте, Кодт, Вы писали:

К>Ещё пример

К>
К>volatile unsigned ready_flag;

К>void update(bool flag)
К>{
К>  ::InterlockedExchange(&ready_flag, flag); // запись атомарная, лочить незачем
К>  cv.notify_one(); // но известить потребителя нужно
К>  // если поменяем на notify_all для второго сценария, - ничего не изменится
К>}

К>void take_ready() // ждёт ready_flag и опускает его
К>{
К>  unique_lock<mutex> l(m); // чисто для протокола - так требует CV
К>  while(!::InterlockedCompareExchange(&ready_flag,false,true)) cv.wait(l);
К>}

К>// первый сценарий
К>void update_nothing() { update(true); update(false); } // дёрнули, даже дважды, но пока поток-приёмник проснётся...

К>// второй сценарий
К>void update_twice() { update(true); update(true); } // дёрнули дважды
К>void thread_one()   { take_ready(); } // первый проснулся и погасил
К>void thread_two()   { take_ready(); } // второй проснулся, а уже погашено
К>


это плохой пример по двум причинам:
1) каша из bool/unsigned. надо на чем-то одном остановиться
2) из-за того, что в void update не лочит мьютекс мы имеем race, из-за которого cv.wait(l) зависнет навечно, хотя ready_flag будет равен true. очень важно понимать, насколько мощен wait:
этот метод одновременно уходит в ожидание и разлочивает мьютекс (транзакционно). это гарантирует, что при правильном использовании (а не как в этом примере) cv, метод notify не уйдет в пустоту. гарантирует, что условие в while не может измениться, пока мы не провалимся в wait. поэтому менять данные, которые могут изменять condition надо строго под мьютексом
Re[2]: C++11: Синхронизация - Условные переменные и ложные п
Здравствуйте, Кодт, Вы писали:

К>Ещё пример

К>
К>volatile unsigned ready_flag;

К>void update(bool flag)
К>{
К>  ::InterlockedExchange(&ready_flag, flag); // запись атомарная, лочить незачем
К>  cv.notify_one(); // но известить потребителя нужно
К>  // если поменяем на notify_all для второго сценария, - ничего не изменится
К>}

К>void take_ready() // ждёт ready_flag и опускает его
К>{
К>  unique_lock<mutex> l(m); // чисто для протокола - так требует CV
К>  while(!::InterlockedCompareExchange(&ready_flag,false,true)) cv.wait(l);
К>}

К>// первый сценарий
К>void update_nothing() { update(true); update(false); } // дёрнули, даже дважды, но пока поток-приёмник проснётся...

К>// второй сценарий
К>void update_twice() { update(true); update(true); } // дёрнули дважды
К>void thread_one()   { take_ready(); } // первый проснулся и погасил
К>void thread_two()   { take_ready(); } // второй проснулся, а уже погашено
К>


это плохой пример по двум причинам:
1) каша из bool/unsigned. надо на чем-то одном остановиться
2) из-за того, что в void update не лочит мьютекс мы имеем race, из-за которого cv.wait(l) зависнет навечно, хотя ready_flag будет равен true. очень важно понимать, насколько мощен wait:
этот метод одновременно уходит в ожидание и разлочивает мьютекс (транзакционно). это гарантирует, что при правильном использовании (а не как в этом примере) cv, метод notify не уйдет в пустоту. гарантирует, что условие в while не может измениться, пока мы не провалимся в wait. поэтому менять данные, которые могут изменять condition, надо строго под мьютексом.