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