C++11多线程:condition_variable头文件

<condition_variable>头文件主要包含了与条件变量相关的功能类:condition_variablecondition_variable_any;枚举类:cv_status;以及功能函数notify_all_at_thread_exit
在这里插入图片描述

1. condition_variable

条件变量可以用来在特定情况下阻塞线程,直到收到通知重新运行线程,该过程是通过unique_lock<mutex>实现的。当条件不满足时,通过调用unique_lock的某个等待函数使当前进程阻塞;等到条件满足后,通过在其他线程中调用同一条件变量的唤醒函数通知当前线程重新启动。若想使用unique_lock以外的可锁定类型,可以参见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 2
thread 0
thread 9
thread 4
thread 6
thread 8
thread 7
thread 5
thread 3
thread 1

(1). (constructor)

在这里插入图片描述
创建一个condition_variable对象,对象不可复制不可转移。

(2). wait

在这里插入图片描述
wait函数分为两种:(1)无条件wait (2)带条件wait

(1) 无条件wait
阻塞当前线程(此前lck需要先将mutex上锁),直到收到通知重新唤醒线程。线程阻塞以后,函数自动调用lck.unlock()释放mutex,使其他等待该互斥量的线程得以继续执行。一旦收到唤醒通知,函数会唤醒当前线程,并通过lck.lock()重新上锁(当mutex被占用时会发生二次阻塞),上锁成功后,lck恢复到函数调用以前的状态,函数返回。

唤醒通知通常是在其他线程中通过调用notify_one或者notify_all实现。使用这些函数时需要确保唤醒条件得到满足,避免出现虚假唤醒通知。

(2) 带条件wait
该版本wait带有判断体pred,只有当pred返回false时线程发生阻塞,并且在收到唤醒通知后,只有pred返回true线程才会被唤醒,这对于防止虚假唤醒通知非常有效。带条件的wait调用等同于:while(!pred()) wait(lck);

示例:

// 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

上例中,cargo作为判断条件。cargo=0时,consume线程阻塞,主线程运行,主线程修改cargo后发出唤醒通知,同时自己yield(让出cpu,并非阻塞)。consume线程被唤醒后输出cargo并重新为其赋值0,阻塞自己等待主线程唤醒。cargo=0后主线程继续运行修改其值并发出唤醒通知。两个线程交替运行,直到输出10个结果。

(3). wait_for和wait_until

工作原理和wait类似,只是分别给wait的时间段和时间点设了限制。区别类似于timed_mutex中的try_lock_fortry_lock_until与mutex中lock的区别。

(4). notify_one

唤醒一个等待该条件变量的阻塞线程。如果没有等待线程,函数不作为,如果有多个等待线程,不明确指定哪个线程被唤醒,即随机唤醒。

示例:

// 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
6
7
8
9
10

上例中,设置了两个条件变量:produce和consume分别作用于生产者线程和消费者线程。cargo代表资源,cargo=0时,消费者阻塞,生产者开始制造,制造完成cargo!=0,此时通知消费者消费;cargo!=0时,生产者阻塞,消费者开始消费,消费完成cargo=0,此时通知生产者生产。

(5). notify_all

唤醒所有等待该条件变量的阻塞线程。如果没有等待线程,函数不作为。

与notufy_one的区别:
notify_one是随机唤醒一个等待中的线程获取锁,不能保证唤醒线程就是想要的线程。notify_all是唤醒所有等待中的线程去竞争锁。线程等待的是notify_*函数而不是锁,notify_one调用后,一个线程被唤醒去获取锁,线程执行完毕释放锁,若没有新的notify_*函数被调用,即使锁空闲,依然没有线程被唤醒。若调用notify_all,所有的等待线程都被唤醒,公平竞争锁,直到所有的线程运行完成。

示例:

// 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 6
thread 2
thread 5
thread 3
thread 4
thread 1
thread 7
thread 0
thread 9
thread 8

2. condition_variable_any

和condition_variable基本一样,唯一的区别在于condition_variable_any可以选用任何类型的锁实现加解锁(condition_variable只能选用unique_lock<mutex>)。除此之外,和condition_variable完全相同。

3. cv_status

枚举类型,指示函数是否由于超时而返回。该类型是condition_variable和condition_variable_any对象中函数wait_forwait_until的返回类型。

定义:enum class cv_status { no_timeout, timeout };

cv_status::no_timeout :函数在规定时间内返回(例如:被notufy_*唤醒)。
cv_status::timeout:函数因超时返回。

4. notufy_all_at_thread_exit

void notify_all_at_thread_exit (condition_variable& cond, unique_lock lck); //调用线程退出时,唤醒所有被cond阻塞的线程。

函数会获取lck中mutex的拥有权,转为函数内部存储,在线程退出时解锁释放,释放之后到退出之前会唤醒所有cond阻塞的线程。等同于以下语句:

lck.unlock();
cond.notify_all();

示例:

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

输出(顺序可能不同):

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

参考

[1]. http://www.cplusplus.com/reference/condition_variable/

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Condition_variableC++11中的一个头文件,它定义了一个同步原语,可以用于在多线程环境中等待一个条件的变化。当线程需要等待某个条件时,它可以通过condition_variable的wait()函数来暂停线程的执行,并释放它所持有的互斥锁,从而可以让其他线程有机会更改条件。当条件发生变化时,其他线程可以通过notify_one()或notify_all()函数来唤醒等待的线程。 ### 回答2: condition_variable头文件是C++标准库中提供的一个用于多线程编程的类,用于实现线程之间的同步。 condition_variable头文件定义了std::condition_variable类,它代表了一个条件变量,用于线程之间的等待和通知。条件变量通常与std::unique_lock<std::mutex>搭配使用,实现线程的同步和互斥。 std::condition_variable类提供了以下几个重要的成员函数: 1. wait():线程调用该函数时会被阻塞,直到其他线程调用notify_one()或notify_all()函数唤醒。需要注意的是,在调用wait()之前,需要先获取一个std::unique_lock<std::mutex>对象,并将其作为wait()函数的参数。 2. notify_one():唤醒一个等待的线程,如果没有等待的线程,则什么也不做。 3. notify_all():唤醒所有等待的线程。 使用条件变量可以实现多个线程之间的同步,典型的应用场景是生产者-消费者模型。在该模型中,生产者线程向一个共享缓冲区中生产数据,而消费者线程从该缓冲区中消费数据。当缓冲区为空时,消费者线程需要等待,直到生产者线程将数据放入缓冲区时,通过notify_one()或notify_all()唤醒消费者线程。 需要注意的是,条件变量的使用需要和互斥量配合使用,以保证在等待和唤醒的过程中能够正确地同步线程间的状态。 ### 回答3: condition_variable头文件是C++标准库中提供的一个用于多线程同步的工具。它定义了一个条件变量类(condition_variable),可以用于在线程之间进行条件等待和唤醒。 条件变量的主要作用是允许一个或多个线程等待某个特定条件成立后再继续执行。在条件变量中,等待线程会自动释放持有的锁,并进入阻塞状态等待条件满足。一旦条件满足,唤醒线程会通知等待线程继续执行。 condition_variable头文件中定义的条件变量类主要包含以下几个成员函数: 1. wait():等待条件满足,线程会阻塞在此处并释放其持有的互斥锁。在条件满足或者收到其他线程的notify_one()或notify_all()通知后,线程会重新获得锁并继续执行。 2. notify_one():随机唤醒一个等待线程,使其继续执行。如果没有等待线程,则什么也不做。 3. notify_all():唤醒所有等待线程,使其继续执行。 使用条件变量需要配合互斥锁(mutex)使用,通常的做法是创建一个互斥锁对象和一个条件变量对象,并在等待条件、修改条件和唤醒线程时使用互斥锁进行保护。 通过条件变量,我们可以实现线程间的同步和协作,例如生产者-消费者模型中,生产者线程通过条件变量通知消费者线程有新的数据可以处理;或者在某个任务完成时,通知其他等待线程继续执行。条件变量提供了一种高效、安全的线程同步机制,可以有效地避免不必要的忙等待,提高了多线程的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值