C++ 多线程 condition变量

首先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前

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值