admin管理员组文章数量:1599543
首先condition变量的常用函数有两个:wait和notify_one[all]:
wait函数:
void wait(unique_lock<mutex>& _Lck)
{ // wait for signal
// Nothing to do to comply with LWG 2135 because std::mutex lock/unlock are nothrow
_Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
}
wait函数接收一个unique_lock,并将其unlock,然后阻塞线程,注意,这点有点怪异,因为unlock总是与解封线程相关联,但这里却是与阻塞线程相关联,这是因为condition变量是一个提供不依赖锁来阻塞线程的工具,但不依赖锁,却为什么要传递一个锁呢?很简单,这是为了防止死锁,因为在wait前,可能该线程获取了多个或一个锁,这回导致在申请该锁的线程与正在wait且获得该锁的线程一起等待,这是我们不希望看见的,所以为了防止这种事情发生,我们需要将该线程的获得的锁传递给condition的wait函数,其中会调用该锁的unlock函数,然后进行等待,直到有线程将其唤醒;
wait_for、wait_until
这两个函数可以放到一起来说,因为提供的功能本质是相同的;
先看源码:
template<class _Rep,class _Period>
cv_status wait_for(unique_lock<mutex>& _Lck,
const chrono::duration<_Rep, _Period>& _Rel_time)
{
// wait for duration
STDEXT threads::xtime _Tgt = _To_xtime(_Rel_time);
return (wait_until(_Lck, &_Tgt));
}
和wait函数一样,接受一个唯一锁,然后是一个时间变量,代表要等待的时间,最后返回一个cv_status代表等待的状态,看cv_status的源码:
enum class cv_status {
// names for wait returns
no_timeout,
timeout
};
一个代表的是在等待的过程中,该线程被唤醒,即no_timeout,另一个相反,即timeout;
wait_until也是一个样子,只不过第二个参数相对与wait_for来说变了罢了;
template<class _Clock,class _Duration>
cv_status wait_until(unique_lock<mutex>& _Lck,
const chrono::time_point<_Clock, _Duration>& _Abs_time)
{ // wait until time point
_STDEXT threads::xtime _Tgt = _To_xtime(_Abs_time - _Clock::now());
return (wait_until(_Lck, &_Tgt));
}
唤醒函数:
notify_one:
假设我们定义了一个condition变量cond,当我们调用cond.notify_one函数的时候,会唤醒一个托管于condition变量的线程[当我们在某一个线程用同一个实例cond进行wait时,该线程就被托管与cond了],当然,被唤醒的是哪一个线程这个结果是不确定的,但是所有线程都会争抢这个机会,即会导致惊群效应;
notify_all:
这个看名字就可以知道了,这个是将托管与cond对象的线程全部唤醒:
两段测试代码:
1:使用notify_none:
#include<iostream>
#include<thread>
#include<condition_variable>
#include<mutex>
class testClass {
std::mutex mtx;
std::condition_variable cond;
std::once_flag once;
public:
void call() {
std::cout << "call: 多个线程进入fun1,但是锁被fun2占用,等待输入将锁释放" << std::endl;
}
void fun1(int i) {
std::call_once(once,&testClass::call,this);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::lock_guard<std::mutex> lock_g(mtx);
std::cout << "fun1: " << i << std::endl;
}
void fun2() {
std::unique_lock<std::mutex> u_lock(mtx);
int x; std::cin >> x;
std::cout << "fun2: 调用wiat方法,阻塞线程的同时将锁解开" << std::endl;
cond.wait(u_lock);
std::cout << "fun2: 解锁完成" << std::endl;
}
void fun3() {
std::cout << "fun3: 输入任意字符调用notify函数,这将结束fun2的等待" << std::endl;
int x; std::cin >> x;
cond.notify_one();
}
};
int main() {
testClass test;
std::thread thSon(&testClass::fun2,&test);
std::thread thSons[10];
for (int i = 0; i < 10; i++) {
thSons[i] = std::thread(&testClass::fun1,&test, i);
}
for (int i = 0; i < 10; i++) {
thSons[i].join();
}
std::thread thSon1(&testClass::fun3,&test);
thSon.join();
thSon1.join();
}
2:使用notify_all函数
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
class testClass {
std::mutex mtx;
std::mutex mtx1;
int id = 0;
int id1 = 0;
std::condition_variable cond;
public:
void print() {
std::unique_lock<std::mutex> lock_u(mtx);
cond.wait(lock_u);
std::cout << "print id " << id << std::endl << '\n';
id++;
}
void print_() {
std::unique_lock<std::mutex> lock_u(mtx1);
cond.wait(lock_u);
std::cout << "prtint id" << id1 << std::endl << '\n';
id1++;
}
void go() {
cond.notify_all();
}
};
int main() {
testClass test;
std::thread thSon[10];
for (int i = 0; i < 10; i++) {
if(i<5)
thSon[i] = std::thread(&testClass::print, &test);
else
thSon[i] = std::thread(&testClass::print_, &test);
}
std::cout << "输入一个字符,启动唤醒函数" << std::endl;
int x; std::cin >> x;
test.go();
for (int i = 0; i < 10; i++) {
thSon[i].join();
}
}
注意点:
1:当一个线程被wake时,中线程会将获得的锁上锁,使线程的状态回到wait前
版权声明:本文标题:C++ 多线程 condition变量 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728321377a1153866.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论