C ++同時実行ガイドstd :: condition_variable

<condition_variable>ヘッダーファイルには、主に条件変数に関連するクラスと関数が含まれています。関連するクラスには、std :: condition_variableとstd :: condition_variable_any、および列挙型std :: cv_statusが含まれます。さらに、関数std :: notify_all_at_thread_exit()も含まれています。上記のタイプを以下に紹介します。

std :: condition_variableの概要

std :: condition_variableオブジェクトの待機関数が呼び出されると、std :: unique_lock(std :: mutex経由)を使用して現在のスレッドをロックします。現在のスレッドは、別のスレッドが同じstd :: condition_variableオブジェクトの通知関数を呼び出して現在のスレッドをウェイクアップするまで、ブロックされます。

std :: condition_variableオブジェクトは通常、std :: unique_lock <std :: mutex>を使用して待機します。別のロック可能なタイプを使用する必要がある場合は、std :: condition_variable_anyクラスを使用できます。std:: condition_variable_anyの使用法について説明します。この記事の後半。

まず、簡単な例を見てみましょう。

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

出力結果:

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

さて、条件変数の基本的な理解ができたので、std :: condition_variableのさまざまなメンバー関数を見てみましょう。

std :: condition_variableコンストラクター

デフォルト condition_variable();
コピー[削除済み] condition_variable(const condition_variable&)=削除;

std :: condition_variableのコピーコンストラクターは無効になっており、デフォルトのコンストラクターのみが提供されています。

std :: condition_variable :: wait()介绍

無条件 void wait(unique_lock <mutex>&lck);
述語 テンプレート<クラス述語> void wait(unique_lock <ミューテックス>&lck、述語pred);

std :: condition_variableは、2つのwait()関数を提供します。現在のスレッドがwait()を呼び出した後、別のスレッドがnotify_ *を呼び出して現在のスレッドをウェイクアップするまで、ブロックされます(この時点で、現在のスレッドはロック(mutex)を取得しているはずなので、ロックlckを設定しましょう)。

スレッドがブロックされると、この関数は自動的にlck.unlock()を呼び出してロックを解放し、ロックの競合でブロックされた他のスレッドが実行を継続できるようにします。さらに、現在のスレッドが通知されると(通知されると、通常は別のスレッドがnotify_ *を呼び出して現在のスレッドをウェイクアップします)、wait()関数も自動的にlck.lock()を呼び出し、lckのステータスを待機関数が呼び出されます。

2番目のケース(つまり、述語が設定されている)では、pred条件がfalseの場合にのみ、wait()を呼び出すと現在のスレッドがブロックされ、他のスレッドからの通知を受信した後、predがtrueの場合にのみブロックが解除されます。したがって、2番目のケースは次のコードに似ています。

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

次の例を考えてみましょう。

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

ここに画像の説明を挿入
std :: condition_variable :: wait()に似ていますが、wait_forで期間を指定できます。現在のスレッドが通知を受信するか、指定された時間rel_timeが期限切れになるまで、スレッドはブロックされます。タイムアウトするか、他のスレッドから通知を受信すると、wait_forが戻り、残りの処理手順はwait()と同様です。

さらに、オーバーロードされたバージョンのwait_for(predicte(2))の最後のパラメーターpredは、wait_forの予測条件を表します。pred条件がfalseの場合にのみ、wait()が呼び出されて現在のスレッドがブロックされ、通知を受信します。他のスレッドからpredがtrueの場合にのみブロックが解除されるため、次のコードと同等です。

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

次の例を参照してください。次の例では、メインスレッドは、thスレッドが値を入力するのを待ってから、thスレッドが端末から受け取った値を出力します。thスレッドが値を受け取る前に、メインスレッドは1秒のタイムアウトを待ちます。一度、「。」を出力します。

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

ここに画像の説明を挿入
std :: condition_variable :: wait_forに似ていますが、wait_untilで時点を指定できます。現在のスレッドが通知を受信するか、指定された時点abs_timeが期限切れになるまで、スレッドはブロックされます。タイムアウトするか、他のスレッドから通知を受信すると、wait_untilが戻り、残りの処理手順はwait_until()と同様です。

さらに、オーバーロードされたバージョンのwait_untilの最後のパラメーターpred(predicte(2))は、wait_untilの予測条件を表します。pred条件がfalseの場合にのみ、wait()を呼び出して現在のスレッドをブロックし、他のスレッドから通知を受信した後predがtrueの場合にのみブロックが解除されるため、次のコードと同等です。

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

std :: condition_variable :: notify_one()介绍

待機中(待機中)のスレッドをウェイクアップします。待機中のスレッドがない場合、関数は何もしません。同時に複数の待機中のスレッドがある場合、特定のスレッドをウェイクアップすることは指定されていません。

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

すべての待機中(待機中)のスレッドをウェイクアップします。待機中のスレッドがない場合、この関数は何もしません。
次の例を考えてみましょう。

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

std :: condition_variable_anyの概要

std :: condition_variableに似ていますが、std :: condition_variable_anyの待機関数はロック可能なパラメーターを受け入れることができますが、std :: condition_variableは、std :: condition_variableに加えて、std :: unique_lockstd :: mutexタイプのパラメーターのみを受け入れることができます。まったく同じ。

std :: cv_status列挙型の概要

ここに画像の説明を挿入

std :: notify_all_at_thread_exit

関数プロトタイプは次のとおりです。

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

関数を呼び出したスレッドが終了すると、cond条件変数を待機しているすべてのスレッドに通知されます。
次の例を考えてみましょう。

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

おすすめ

転載: blog.csdn.net/qq_24649627/article/details/114322040