C ++ Concurrency Guide std :: condition_variable

Die Header-Datei <condition_variable> enthält hauptsächlich Klassen und Funktionen, die sich auf Bedingungsvariablen beziehen. Verwandte Klassen sind std :: condition_variable und std :: condition_variable_any sowie der Aufzählungstyp std :: cv_status. Darüber hinaus enthält es auch die Funktion std :: notify_all_at_thread_exit (). Die oben genannten Typen werden im Folgenden vorgestellt.

Einführung in std :: condition_variable

Wenn eine Wartefunktion des Objekts std :: condition_variable aufgerufen wird, verwendet sie std :: unique_lock (über std :: mutex), um den aktuellen Thread zu sperren. Der aktuelle Thread wird blockiert, bis ein anderer Thread die Benachrichtigungsfunktion für dasselbe std :: condition_variable-Objekt aufruft, um den aktuellen Thread zu aktivieren.

Das Objekt std :: condition_variable verwendet normalerweise std :: unique_lock <std :: mutex> zum Warten. Wenn Sie einen anderen sperrbaren Typ verwenden müssen, können Sie die Klasse std :: condition_variable_any verwenden. Die Verwendung von std :: condition_variable_any wird erläutert später in diesem Artikel.

Schauen wir uns zunächst ein einfaches Beispiel an:

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

Ausgabeergebnis:

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

Okay, jetzt, da wir ein grundlegendes Verständnis der Bedingungsvariablen haben, werfen wir einen Blick auf die verschiedenen Elementfunktionen von std :: condition_variable.

Konstruktor std :: condition_variable

Standard condition_variable ();
Kopie [gelöscht] condition_variable (const condition_variable &) = löschen;

Der Kopierkonstruktor von std :: condition_variable ist deaktiviert und nur der Standardkonstruktor wird bereitgestellt.

std :: condition_variable :: wait () 介绍

bedingungslos void wait (unique_lock <Mutex> & lck);
Prädikat template <class Predicate> void wait (unique_lock <mutex> & lck, Predicate pred);

std :: condition_variable bietet zwei wait () - Funktionen. Nachdem die aktuellen Thread-Aufrufe wait () aufgerufen haben, wird sie blockiert (zu diesem Zeitpunkt sollte der aktuelle Thread die Sperre (Mutex) erhalten haben, setzen wir also die Sperre lck), bis ein anderer Thread notify_ * aufruft, um den aktuellen Thread zu aktivieren.

Wenn ein Thread blockiert ist, ruft diese Funktion automatisch lck.unlock () auf, um die Sperre aufzuheben, damit andere Threads, die bei Sperrenkonflikten blockiert werden, weiterhin ausgeführt werden können. Sobald der aktuelle Thread benachrichtigt wird (benachrichtigt, normalerweise ruft ein anderer Thread notify_ * auf, um den aktuellen Thread zu aktivieren), ruft die Funktion wait () automatisch lck.lock () auf, sodass der Status von lck der gleiche ist wie beim Wartefunktion wird aufgerufen.

Im zweiten Fall (dh Prädikat ist festgelegt) blockiert call wait () nur dann, wenn die pred-Bedingung falsch ist, den aktuellen Thread und wird nur dann entsperrt, wenn pred true ist, nachdem eine Benachrichtigung von anderen Threads empfangen wurde. Der zweite Fall ähnelt also dem folgenden Code:

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

Betrachten Sie das folgende Beispiel:

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

Fügen Sie hier eine Bildbeschreibung ein
Ähnlich wie std :: condition_variable :: wait (), aber wait_for kann einen Zeitraum angeben. Der Thread wird blockiert, bis der aktuelle Thread eine Benachrichtigung erhält oder die angegebene Zeit rel_time abläuft. Sobald das Zeitlimit überschritten ist oder eine Benachrichtigung von anderen Threads empfangen wird, kehrt wait_for zurück und die verbleibenden Verarbeitungsschritte ähneln wait ().

Außerdem repräsentiert der letzte Parameter pred der überladenen Version von wait_for (predicte (2)) die Vorhersagebedingung von wait_for. Nur wenn die pred-Bedingung falsch ist, wird wait () aufgerufen, um den aktuellen Thread zu blockieren, und nach dem Empfang von Benachrichtigungen von anderen Threads Nur wenn pred true ist, wird es entsperrt, sodass es dem folgenden Code entspricht:

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

Bitte sehen Sie sich das folgende Beispiel an. Im folgenden Beispiel wartet der Haupt-Thread darauf, dass der dritte Thread einen Wert eingibt, und druckt dann den Wert aus, den der dritte Thread vom Terminal empfangen hat. Bevor der dritte Thread den Wert empfängt, wird der Haupt-Thread wartet auf eine Zeitüberschreitung von einer Sekunde. Einmal und drucke ein ".":

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

Fügen Sie hier eine Bildbeschreibung ein
Ähnlich wie std :: condition_variable :: wait_for, aber wait_until kann einen Zeitpunkt angeben. Der Thread wird blockiert, bis der aktuelle Thread eine Benachrichtigung erhält oder der angegebene Zeitpunkt abs_time abläuft. Sobald das Zeitlimit überschritten ist oder eine Benachrichtigung von anderen Threads empfangen wird, kehrt wait_until zurück und die verbleibenden Verarbeitungsschritte ähneln wait_until ().

Darüber hinaus stellt der letzte Parameter pred der überladenen Version von wait_until (predicte (2)) die Vorhersagebedingung von wait_until dar. Nur wenn die pred-Bedingung falsch ist, wird wait () den aktuellen Thread blockieren und nach dem Empfang von Benachrichtigungen von anderen Threads Nur wenn pred true ist, wird es entsperrt, sodass es dem folgenden Code entspricht:

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

std :: condition_variable :: notify_one () 介绍

Wecken Sie einen wartenden Thread. Wenn kein wartender Thread vorhanden ist, führt die Funktion nichts aus. Wenn mehrere wartende Threads gleichzeitig vorhanden sind, ist es nicht angegeben, einen bestimmten Thread zu aktivieren.

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

Wecken Sie alle wartenden (Warte-) Threads auf. Wenn kein wartender Thread vorhanden ist, führt diese Funktion nichts aus.
Betrachten Sie das folgende Beispiel:

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

Einführung in std :: condition_variable_any

Ähnlich wie bei std :: condition_variable, außer dass die Wartefunktion von std :: condition_variable_any jeden sperrbaren Parameter akzeptieren kann, während std :: condition_variable zusätzlich zu std :: condition_variable nur Parameter vom Typ std :: unique_lockstd :: mutex akzeptieren kann genauso.

Einführung in den Aufzählungstyp std :: cv_status

Fügen Sie hier eine Bildbeschreibung ein

std :: notify_all_at_thread_exit

Der Funktionsprototyp lautet:

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

Wenn der Thread, der die Funktion aufgerufen hat, beendet wird, werden alle Threads benachrichtigt, die auf die Bedingungsbedingungsvariable warten.
Betrachten Sie das folgende Beispiel:

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

Ich denke du magst

Origin blog.csdn.net/qq_24649627/article/details/114322040
Empfohlen
Rangfolge