Digamos que eu tenha um monte de threads de trabalho esperando até que haja tarefas na fila.
Geralmente, você faz o produtor chamar a função de produção e, em seguida, notify_one
para que o thread de trabalho possa desbloquear e continuar.
No entanto, digamos que quando o produtor produziu, não há threads esperando no condition_variable
. O produtor notificará, o que não fará nada. Então, um consumidor chega no condition_variable
e wait
s, mas perdeu a notificação anterior.
Agora você tem uma tarefa em uma fila até que o produtor chame notificar NOVAMENTE, momento em que haverá duas tarefas na fila.
Minha pergunta é: como posso evitar que haja trabalhos na fila de espera quando há threads disponíveis?
Exemplo:
struct ThreadPool
{
static inline std::mutex mutex;
static inline std::condition_variable condition_var;
static inline int number_of_things_to_consume = 0;
static void consume()
{
std::unique_lock lock{ mutex };
/* THE WORKER THREAD COULD BE WAITING HERE WITH SOMETHING IN THE QUEUE
- BECAUSE THE PRODUCER CALLED NOTIFY ONE BUT NO THREAD WAS WAITING YET */
condition_var.wait(lock, []() {return number_of_things_to_consume > 0; });
/* CONSUME */
}
static void produce()
{
std::unique_lock lock{ mutex };
++number_of_things_to_consume;
condition_var.notify_one();
}
};
Conclusão:
Já que você usou a
wait
com um predicado, sua preocupação é infundada.Seu código está OK e não haverá tal cenário.
Mais algumas informações:
Como você pode ver na documentação , usar
wait
com um predicado como você fez é equivalente a:Então, se o predicado for verdadeiro (porque o produtor adicionou um job), o consumidor não vai esperar nada.
E como o produtor está sempre atualizando o predicado sob o bloqueio (como deveria), o consumidor não vai sentir falta dele caso ele tenha entrado no
wait
.Uma observação lateral:
Usar um predicado em vez de um bare
wait
também é recomendado para lidar com wakeups espúrios . Então, em geral, é recomendado sempre usar um predicado comwait
.