Здесь необходимо ввести больше, чем просто условную переменную, чтобы избежать нескольких различных состояний гонки. Также требуются мьютекс и флаг завершения задания.
На этом этапе становится проще заменить ваш std::function<void()>
небольшим классом, который содержит это закрытие, а также весь дополнительный багаж:
struct job {
std::function<void()> implementation;
std::mutex m;
std::condition_variable flag;
bool completed=false;
};
Ваша очередь становится очередью std::shared_ptr<job>
s вместо очереди std::function
s, с заданиями, созданными в области динамической c (поскольку, конечно, мьютексы и переменные условия не копируются или подвижный, и эти объекты доступны из обоих ваших потоков).
После того, как ваш рабочий поток завершит выполнение реализации , он:
- блокирует мьютекс .
- устанавливает
completed
в значение true - сигнализирует об условной переменной.
И ваш PushAndWaitUntilExecuted
, после того, как он выполняет pu sh:
- блокирует мьютекс
- ожидает в переменной условия, пока не будет установлено
completed
Вы должны полностью понимать, что C ++ не дает вам абсолютно никаких гарантий, вообще , что после пу sh новое закрытие в вашу очередь заданий, какой-то поток не сразу захватывает его, не выполняет и не завершает sh до того, как исходный поток (тот, который его подтолкнул) не успеет посмотреть на переменную условия. К настоящему времени никто больше не будет сигнализировать об условной переменной. Если все, с чем вам нужно работать здесь, это просто переменная условия, вы будете ждать, пока переменная условия не получит сигнал, пока наше солнце не взорвется.
Вот почему вам нужно больше, чем просто переменная условия, a мьютекс и явный флаг, и используйте описанный выше подход для правильной обработки межпотоковой последовательности.
Это довольно классический рутинный подход. Вы должны найти примеры многих подобных реализаций в каждом хорошем учебнике C ++ по этому предмету.