Guía de concurrencia de C ++ std :: condition_variable

El archivo de encabezado <condition_variable> contiene principalmente clases y funciones relacionadas con variables de condición. Las clases relacionadas incluyen std :: condition_variable y std :: condition_variable_any, así como el tipo enumerado std :: cv_status. Además, también incluye la función std :: notify_all_at_thread_exit (). Los tipos anteriores se presentan a continuación.

Introducción a std :: condition_variable

Cuando se llama a una función de espera del objeto std :: condition_variable, usa std :: unique_lock (a través de std :: mutex) para bloquear el hilo actual. El hilo actual se bloqueará hasta que otro hilo llame a la función de notificación en el mismo objeto std :: condition_variable para despertar el hilo actual.

El objeto std :: condition_variable usualmente usa std :: unique_lock <std :: mutex> para esperar. Si necesita usar otro tipo bloqueable, puede usar la clase std :: condition_variable_any. Se discutirá el uso de std :: condition_variable_any más adelante en este artículo.

Primero veamos un ejemplo simple:

#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 salida:

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

Bien, ahora que tenemos una comprensión básica de las variables de condición, echemos un vistazo a las diversas funciones miembro de std :: condition_variable.

std :: condition_variable constructor

defecto condition_variable ();
copiar [eliminado] condition_variable (const condition_variable &) = eliminar;

El constructor de copia de std :: condition_variable está deshabilitado y solo se proporciona el constructor predeterminado.

std :: condition_variable :: esperar () 介绍

incondicional espera vacía (bloqueo_único <mutex> & lck);
predicado plantilla <class Predicate> void wait (unique_lock <mutex> & lck, Predicate pred);

std :: condition_variable proporciona dos funciones wait (). Después de que el hilo actual llame a wait (), se bloqueará (en este momento, el hilo actual debería haber adquirido el bloqueo (mutex), así que establezcamos el bloqueo lck) hasta que otro hilo llame a notify_ * para despertar el hilo actual.

Cuando un hilo está bloqueado, esta función llamará automáticamente a lck.unlock () para liberar el bloqueo, de modo que otros hilos que están bloqueados en la contención de bloqueo puedan continuar ejecutándose. Además, una vez que se notifica al hilo actual (se le notifica, normalmente otro hilo llama a notify_ * para despertar el hilo actual), la función wait () también llama automáticamente a lck.lock (), haciendo que el estado de lck sea el mismo que cuando el Se llama a la función de espera.

En el segundo caso (es decir, Predicate está establecido), solo cuando la condición pred es falsa, la llamada wait () bloqueará el hilo actual y se desbloqueará solo cuando pred sea verdadero después de recibir una notificación de otros hilos. Entonces, el segundo caso es similar al siguiente código:

while (!pred()) wait(lck);

Considere el siguiente ejemplo:

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

Inserte la descripción de la imagen aquí
Similar a std :: condition_variable :: wait (), pero wait_for puede especificar un período de tiempo. El hilo se bloqueará hasta que el hilo actual reciba una notificación o el tiempo especificado rel_time expire. Una vez que se agota el tiempo de espera o recibe una notificación de otros subprocesos, wait_for devuelve, y los pasos de procesamiento restantes son similares a wait ().

Además, el último parámetro pred de la versión sobrecargada de wait_for (predicte (2)) representa la condición de predicción de wait_for. Solo cuando la condición pred sea falsa, se llamará a wait () para bloquear el hilo actual y después de recibir notificaciones. de otros hilos Solo cuando pred sea verdadero se desbloqueará, por lo que es equivalente al siguiente código:

return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));

Por favor, vea el siguiente ejemplo. En el siguiente ejemplo, el subproceso principal espera que el subproceso ingrese un valor, y luego imprime el valor recibido por el subproceso del terminal. Antes de que el subproceso reciba el valor, el subproceso principal espera un tiempo de espera de un segundo. Una vez e imprime un ".":

#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 介绍

Inserte la descripción de la imagen aquí
Similar a std :: condition_variable :: wait_for, pero wait_until puede especificar un punto de tiempo. El hilo se bloqueará hasta que el hilo actual reciba una notificación o el punto de tiempo abs_time especificado expire. Una vez que se agota el tiempo de espera o recibe una notificación de otros subprocesos, wait_until regresa, y los pasos de procesamiento restantes son similares a wait_until ().

Además, el último parámetro pred de la versión sobrecargada de wait_until (predicte (2)) representa la condición predictiva de wait_until. Solo cuando la condición pred sea falsa, llamar a wait () bloqueará el hilo actual y después de recibir notificaciones de otros hilos. Solo cuando pred sea verdadero se desbloqueará, por lo que es equivalente al siguiente código:

while (!pred())
  if ( wait_until(lck,abs_time) == cv_status::timeout)
    return pred();
return true;

std :: condition_variable :: notify_one () 介绍

Despierta un hilo de espera (espera). Si no hay un subproceso en espera, la función no hace nada. Si hay varios subprocesos en espera al mismo tiempo, no se especifica que despierte un determinado subproceso.

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

Despierta todos los hilos de espera (espera). Si no hay ningún hilo en espera, esta función no hace nada.
Considere el siguiente ejemplo:

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

Introducción a std :: condition_variable_any

Similar a std :: condition_variable, excepto que la función de espera de std :: condition_variable_any puede aceptar cualquier parámetro bloqueable, mientras que std :: condition_variable solo puede aceptar parámetros de tipo std :: unique_lockstd :: mutex, además de std :: condition_variable Es casi exactamente lo mismo.

Introducción al tipo de enumeración std :: cv_status

Inserte la descripción de la imagen aquí

std :: notify_all_at_thread_exit

El prototipo de función es:

void notify_all_at_thread_exit (condition_variable& cond, unique_lock<mutex> lck);

Cuando el subproceso que llamó a la función sale, se notificará a todos los subprocesos que esperan en la variable de condición cond.
Considere el siguiente ejemplo:

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

Supongo que te gusta

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