Guia de simultaneidade C ++ std :: condition_variable

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 () 介绍

Insira a descrição da imagem aqui
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 介绍

Insira a descrição da imagem aqui
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

Insira a descrição da imagem aqui

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;
}

Acho que você gosta

Origin blog.csdn.net/qq_24649627/article/details/114322040
Recomendado
Clasificación