admin管理员组文章数量:1599529
<condition_variable>头文件主要包含了与条件变量相关的功能类:condition_variable
和condition_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_for
、try_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_for
和wait_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/reference/condition_variable/
本文标签: 多线程头文件conditionvariable
版权声明:本文标题:C++11多线程:condition_variable头文件 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1728322518a1154010.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论