Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, netch80, Вы писали:
N>>Пусть задача TB закончила выполнение очередной job и стала в очередь на мьютекс. Задача TC поставила новый job в пустую очередь и отпустила мьютекс. TC делает notify_one(), шедулер дёргает проснуться TA (TB сейчас не в ожидании, на неё не поставят). TA становится в очередь на мьютекс. TC отпускает мьютекс (до notify_one() или после — почти без разницы), TB захватывает мьютекс, начинает выполняться, забирает задание, очередь становится пустой. TA приходит за заданием, а его нет — TB забрала раньше. TA идёт спать на новый круг.
BFE>Это описание выглядит как описание типичной ошибки race condition: два независимых события 'пробудить очередь' и 'отдать-захватить мьютекс' не синхронизированы.
Конкретно в этом примере нет гонки с пробуждением. Он же позвал notify_one внутри захвата мьютекса. Ядро в ответ на вызов notify_one тут же будит поток (который попадает в планировщик), и лишь потом отпускает мьютекс. Что это тогда, если не самая лучшая синхронизация? :)
Если другая проблема: потоки могут тупить по сотне разных причин. То им процессора не достанется, или достанется, но их другой поток тут же вытеснит, то page fault случится. В результате задание из очереди получает более расторопный поток (и это хорошо). И по большому счёту эта ситуация эквивалентна другой ситуации, в которой нет никаких условных переменных и сигналов пробуждения, а просто есть два потока-исполнителя стартующих одновременно и единственное задание в очереди. Никто же не называет гонкой ситуацию, что задание может достаться одному исполнителю или другому:
std::vector<int> v = {{1}};
std::for_each(std::execution::parallel_policy, begin(v), end(v), fn);
— не гонка, что fn будет вызван в случайном потоке из пула.