O arquivo de cabeçalho <condition_variable> contém principalmente classes e funções relacionadas às variáveis de condição. As classes relacionadas incluem std :: condition_variable e std :: condition_variable_any, bem como o tipo enumerado std :: cv_status. Além disso, também inclui a função std :: Notice_all_at_thread_exit (). Os tipos acima são apresentados a seguir.
Introdução a std :: condition_variable
Quando uma função de espera do objeto std :: condition_variable é chamada, ela usa std :: unique_lock (via std :: mutex) para bloquear o segmento atual. O thread atual será bloqueado até que outro thread chame a função de notificação no mesmo objeto std :: condition_variable para ativar o thread atual.
O objeto std :: condition_variable geralmente usa std :: unique_lock <std :: mutex> para esperar. Se você precisar usar outro tipo bloqueável, você pode usar a classe std :: condition_variable_any. O uso de std :: condition_variable_any será discutido posteriormente neste artigo.
Vejamos primeiro um exemplo simples:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
void do_print_id(int id)
{
std::unique_lock <std::mutex> lck(mtx);
while (!ready) // 如果标志位不为 true, 则等待...
cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
// 线程被唤醒, 继续往下执行打印线程编号id.
std::cout << "thread " << id << '\n';
}
void go()
{
std::unique_lock <std::mutex> lck(mtx);
ready = true; // 设置全局标志位为 true.
cv.notify_all(); // 唤醒所有线程.
}
int main()
{
std::thread threads[10]; // spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(do_print_id, i);
std::cout << "10 threads ready to race...\n";
go(); // go!
for (auto & th:threads)
th.join();
return 0;
}
Resultado de saída:
10 threads ready to race...
thread 8
thread 2
thread 1
thread 4
thread 5
thread 7
thread 9
thread 0
thread 3
thread 6
Ok, agora que temos uma compreensão básica das variáveis de condição, vamos dar uma olhada nas várias funções-membro de std :: condition_variable.
construtor std :: condition_variable
padrão | condição_variável (); |
---|---|
copiar [excluído] | variável_condição (const condição_variável &) = deletar; |
O construtor de cópia de std :: condition_variable é desabilitado e apenas o construtor padrão é fornecido.
std :: condition_variable :: wait () 介绍
incondicional | void wait (unique_lock <mutex> & lck); |
---|---|
predicado | template <class Predicate> void wait (unique_lock <mutex> & lck, Predicate pred); |
std :: condition_variable fornece duas funções wait (). Depois que o thread atual chamar wait (), ele será bloqueado (neste momento, o thread atual deve ter adquirido o bloqueio (mutex), então vamos definir o bloqueio lck) até que outro thread chame notificar_ * para despertar o thread atual.
Quando um encadeamento é bloqueado, esta função chama automaticamente lck.unlock () para liberar o bloqueio, para que outros encadeamentos que estão bloqueados na contenção do bloqueio possam continuar a executar. Além disso, uma vez que o segmento atual é notificado (notificado, normalmente outro segmento chama notificar_ * para acordar o segmento atual), a função wait () também chama automaticamente lck.lock (), tornando o status de lck o mesmo de quando o a função de espera é chamada.
No segundo caso (isto é, Predicado é definido), apenas quando a condição pred for falsa, a chamada wait () bloqueará o encadeamento atual e será desbloqueado apenas quando pred for verdadeiro após receber notificação de outros encadeamentos. Portanto, o segundo caso é semelhante ao seguinte código:
while (!pred()) wait(lck);
Considere o seguinte exemplo:
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
int cargo = 0;
bool shipment_available()
{
return cargo != 0;
}
// 消费者线程.
void consume(int n)
{
for (int i = 0; i < n; ++i) {
std::unique_lock <std::mutex> lck(mtx);
cv.wait(lck, shipment_available);
std::cout << cargo << '\n';
cargo = 0;
}
}
int main()
{
std::thread consumer_thread(consume, 10); // 消费者线程.
// 主线程为生产者线程, 生产 10 个物品.
for (int i = 0; i < 10; ++i) {
while (shipment_available())
std::this_thread::yield();
std::unique_lock <std::mutex> lck(mtx);
cargo = i + 1;
cv.notify_one();
}
consumer_thread.join();
return 0;
}
std :: condition_variable :: wait_for () 介绍
Semelhante a std :: condition_variable :: wait (), mas wait_for pode especificar um período de tempo. O thread será bloqueado até que o thread atual receba uma notificação ou o tempo especificado rel_time expire. Uma vez que ele atinge o tempo limite ou recebe notificação de outras threads, wait_for retorna e as etapas de processamento restantes são semelhantes a wait ().
Além disso, o último parâmetro pred da versão sobrecarregada de wait_for (predicte (2)) representa a condição de predição de wait_for. Somente quando a condição pred for falsa, wait () será chamado para bloquear o thread atual e após receber notificações de outros threads Somente quando pred for true ele será desbloqueado, portanto, é equivalente ao seguinte código:
return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
Por favor, veja o exemplo a seguir. No exemplo a seguir, a thread principal espera que a thread principal insira um valor e, em seguida, imprime o valor recebido pela thread principal do terminal. Antes que a thread principal receba o valor, a thread principal aguarda o tempo limite de um segundo. Uma vez e imprima um ".":
#include <iostream> // std::cout
#include <thread> // std::thread
#include <chrono> // std::chrono::seconds
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status
std::condition_variable cv;
int value;
void do_read_value()
{
std::cin >> value;
cv.notify_one();
}
int main ()
{
std::cout << "Please, enter an integer (I'll be printing dots): \n";
std::thread th(do_read_value);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
while (cv.wait_for(lck,std::chrono::seconds(1)) == std::cv_status::timeout) {
std::cout << '.';
std::cout.flush();
}
std::cout << "You entered: " << value << '\n';
th.join();
return 0;
}
std :: condition_variable :: wait_until 介绍
Semelhante a std :: condition_variable :: wait_for, mas wait_until pode especificar um ponto de tempo. O segmento será bloqueado até que o segmento atual receba uma notificação ou o ponto de tempo especificado abs_time expire. Depois que ele atinge o tempo limite ou recebe notificação de outros threads, wait_until retorna e as etapas de processamento restantes são semelhantes a wait_until ().
Além disso, o último parâmetro pred da versão sobrecarregada de wait_until (predicte (2)) representa a condição preditiva de wait_until. Somente quando a condição pred for falsa chamará wait () para bloquear o encadeamento atual e, após receber notificações de outros encadeamentos Somente quando pred for true ele será desbloqueado, por isso é equivalente ao seguinte código:
while (!pred())
if ( wait_until(lck,abs_time) == cv_status::timeout)
return pred();
return true;
std :: condition_variable :: notification_one () 介绍
Desperte um thread em espera (esperar). Se não houver nenhum thread em espera, a função não fará nada. Se houver vários threads em espera ao mesmo tempo, não é especificado para ativar um determinado thread.
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
int cargo = 0; // shared value by producers and consumers
void consumer()
{
std::unique_lock < std::mutex > lck(mtx);
while (cargo == 0)
cv.wait(lck);
std::cout << cargo << '\n';
cargo = 0;
}
void producer(int id)
{
std::unique_lock < std::mutex > lck(mtx);
cargo = id;
cv.notify_one();
}
int main()
{
std::thread consumers[10], producers[10];
// spawn 10 consumers and 10 producers:
for (int i = 0; i < 10; ++i) {
consumers[i] = std::thread(consumer);
producers[i] = std::thread(producer, i + 1);
}
// join them back:
for (int i = 0; i < 10; ++i) {
producers[i].join();
consumers[i].join();
}
return 0;
}
std :: condition_variable :: notification_all () 介绍
Desperte todos os threads em espera (aguardar). Se não houver thread de espera, esta função não fará nada.
Considere o seguinte exemplo:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
void do_print_id(int id)
{
std::unique_lock <std::mutex> lck(mtx);
while (!ready) // 如果标志位不为 true, 则等待...
cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
// 线程被唤醒, 继续往下执行打印线程编号id.
std::cout << "thread " << id << '\n';
}
void go()
{
std::unique_lock <std::mutex> lck(mtx);
ready = true; // 设置全局标志位为 true.
cv.notify_all(); // 唤醒所有线程.
}
int main()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(do_print_id, i);
std::cout << "10 threads ready to race...\n";
go(); // go!
for (auto & th:threads)
th.join();
return 0;
}
Introdução a std :: condition_variable_any
Semelhante a std :: condition_variable, exceto que a função de espera de std :: condition_variable_any pode aceitar qualquer parâmetro bloqueável, enquanto std :: condition_variable só pode aceitar parâmetros do tipo std :: unique_lockstd :: mutex, além de std :: condition_variable É quase exatamente o mesmo.
Introdução ao tipo de enumeração std :: cv_status
std :: Notice_all_at_thread_exit
O protótipo da função é:
void notify_all_at_thread_exit (condition_variable& cond, unique_lock<mutex> lck);
Quando o encadeamento que chamou a função for encerrado, todos os encadeamentos aguardando a variável de condição cond serão notificados.
Considere o seguinte exemplo:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id (int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) cv.wait(lck);
// ...
std::cout << "thread " << id << '\n';
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
std::notify_all_at_thread_exit(cv,std::move(lck));
ready = true;
}
int main ()
{
std::thread threads[10];
// spawn 10 threads:
for (int i=0; i<10; ++i)
threads[i] = std::thread(print_id,i);
std::cout << "10 threads ready to race...\n";
std::thread(go).detach(); // go!
for (auto& th : threads) th.join();
return 0;
}