关于c++:C并发与多线程-8conditionvariablewaitnotifyonenotifyall

47次阅读

共计 4185 个字符,预计需要花费 11 分钟才能阅读完成。

std::condition_variable

  • 条件变量是一个对象,该对象可能阻塞调用线程,直到被告诉复原。
  • 当调用其期待函数(wait,wait_for,wait_until)之一时,它应用 unique_lock(通过互斥锁)来锁定线程,该线程将放弃阻塞状态,直到被另一个同在 condition_variable 对象上调用告诉性能的线程唤醒为止。
  • condition_variable 类型的对象始终应用 unique_lock<mutex> 期待(无关可与任何类型的可锁定类型一起应用的代替办法,可参见 condition_variable_any)。
// condition_variable example
#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);
  ready = true;
  cv.notify_all();}

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";
  go();                       // go!

  for (auto& th : threads) th.join();

  return 0;
}

输入:

10 threads ready to race...
thread 5
thread 6
thread 0
thread 9
thread 4
thread 1
thread 3
thread 2
thread 8
thread 7

std::condition_variable::notify_one

void notify_one() noexcept;
  • 解锁以后正在期待此条件的其中一个线程
  • 如果没有线程在期待,则该函数将不执行任何操作(不产生任何影响)
  • 如果超过一个线程在期待,则未指定抉择哪个线程
// 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 produce,consume;

int cargo = 0;     // shared value by producers and consumers

void consumer () {std::unique_lock<std::mutex> lck(mtx);
  while (cargo==0) consume.wait(lck);
  std::cout << cargo << '\n';
  cargo=0;
  produce.notify_one();}

void producer (int id) {std::unique_lock<std::mutex> lck(mtx);
  while (cargo!=0) produce.wait(lck);
  cargo = id;
  consume.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;
}

输入:

1
2
3
4
5
7
6
9
10
8

std::condition_variable::notify_all

void notify_all() noexcept;
  • 解锁以后正在期待此条件的所有线程
  • 如果没有线程在期待,则该函数不执行任何操作(不产生任何影响)
// 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 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);
  ready = true;
  cv.notify_all();}

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";
  go();                       // go!

  for (auto& th : threads) th.join();

  return 0;
}

输入:

10 threads ready to race...
thread 7
thread 4
thread 3
thread 2
thread 0
thread 1
thread 6
thread 5
thread 8
thread 9

std::condition_variable::wait

unconditional (1)
void wait (unique_lock<mutex>& lck);

predicate (2)
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);
  • 以后线程(应以锁定 lck 的互斥对象)的执行被阻塞,直到失去告诉;
  • 在阻塞线程的时刻,该函数主动调用 lck.unlock(),从而容许其它锁定的线程继续执行;
  • 一旦失去告诉(明确的由其它线程告诉),该函数将勾销阻塞并调用 lck.lock(), 使 lck 处于与调用该函数时雷同的状态。而后函数返回(留神,最初一次互斥锁可能会在返回之前再次阻塞线程);
  • 通常,通过另一个线程对成员 notify_one 或 notify_all 的调用来告诉该函数唤醒。然而某些实现可能会产生虚伪的唤醒调用,而不会调用这些函数中的任何一个。因而,应用此性能的用户应确保满足其复原条件;
  • 如果指定了 pred(2),则该函数仅在 pred 返回 false 时才阻塞,并且告诉只能在线程变为 true 时才勾销阻塞线程(这对查看虚伪唤醒调用特地有用)

    • 此版本 (2) 的行为就像是实现为:while (!pred()) wait(lck);

参数阐明

  • lck

    • 一个 unique_lock 对象,其互斥对象以后已被该线程锁定
    • 该对象的所有期待成员函数的所有并发调用均应应用雷同的根底互斥对象(由 lck.mutex() 返回)
  • pred

    • 可调用的对象或函数,不带任何参数,并返回能够作为 bool 值评估的值

    重复调用它,直到评估值未 true

// condition_variable::wait (with predicate)
#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);
    // consume:
    std::cout << cargo << '\n';
    cargo=0;
  }
}

int main ()
{std::thread consumer_thread (consume,10);

  // produce 10 items when needed:
  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;
}

输入:

1
2
3
4
5
6
7
8
9
10

正文完
 0