多线程,condition_variable的使用,条件变量

     ~~~~     刚刚本来想写一下信号量,同时看一下标准库里面的信号量代码,但是却突然发现std里面没有找到信号量,只有条件变量和锁,于是我去网上查一下,为什么没有信号量,就发现有人说,信号量完全可以通过锁加条件变量,实现,所以std里面就没有加入信号量了。
     ~~~~     条件变量是需要和锁同时使用的,在多线程中,锁是可以解决线程同步的,但是有些情况不太高效。
     ~~~~     最经典的就是生产者和消费者模型实现的时候,我们在一个线程中插入数据,一个线程中接受数据,两个线程同时操作线程,我们就需要对线程加锁,生产者只需要加锁,等待,然后写入数据就可以了,但是消费者需要解锁,判断是否有数据,然后解锁,这里如果不使用等待,就是占用大量的系统资源,而使用了等待,就会无法及时获取数据,造成一定的延时,这个时候条件变量就出来了,我这里举例的是std下的条件变量,下面是源码:


class condition_variable
	{	// class for waiting for conditions
public:
	typedef _Cnd_t native_handle_type;

	condition_variable()
		{	// construct
		_Cnd_init_in_situ(_Mycnd());
		}

	~condition_variable() noexcept
		{	// destroy
		_Cnd_destroy_in_situ(_Mycnd());
		}

	condition_variable(const condition_variable&) = delete;
	condition_variable& operator=(const condition_variable&) = delete;

	void notify_one() noexcept
		{	// wake up one waiter
		_Cnd_signalX(_Mycnd());
		}

	void notify_all() noexcept
		{	// wake up all waiters
		_Cnd_broadcastX(_Mycnd());
		}

	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());
		}

	template<class _Predicate>
		void wait(unique_lock<mutex>& _Lck, _Predicate _Pred)
		{	// wait for signal and test predicate
		while (!_Pred())
			wait(_Lck);
		}

	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));
		}

	template<class _Rep,
		class _Period,
		class _Predicate>
		bool wait_for(
			unique_lock<mutex>& _Lck,
			const chrono::duration<_Rep, _Period>& _Rel_time,
			_Predicate _Pred)
		{	// wait for signal with timeout and check predicate
		_STDEXT threads::xtime _Tgt = _To_xtime(_Rel_time);
		return (_Wait_until1(_Lck, &_Tgt, _Pred));
		}

	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));
		}

	template<class _Clock,
		class _Duration,
		class _Predicate>
		bool wait_until(
			unique_lock<mutex>& _Lck,
			const chrono::time_point<_Clock, _Duration>& _Abs_time,
			_Predicate _Pred)
		{	// wait for signal with timeout and check predicate
		_STDEXT threads::xtime _Tgt = _To_xtime(_Abs_time - _Clock::now());
		return (_Wait_until1(_Lck, &_Tgt, _Pred));
		}

	cv_status wait_until(
		unique_lock<mutex>& _Lck,
		const xtime *_Abs_time)
		{	// wait for signal with timeout
		if (!_Mtx_current_owns(_Lck.mutex()->_Mymtx()))
			_Throw_Cpp_error(_OPERATION_NOT_PERMITTED);

		// Nothing to do to comply with LWG 2135 because std::mutex lock/unlock are nothrow
		const int _Res = _Cnd_timedwaitX(_Mycnd(),
			_Lck.mutex()->_Mymtx(), _Abs_time);
		return (_Res == _Thrd_timedout
			? cv_status::timeout : cv_status::no_timeout);
		}

	template<class _Predicate>
		bool wait_until(
			unique_lock<mutex>& _Lck,
			const xtime *_Abs_time,
			_Predicate _Pred)
		{	// wait for signal with timeout and check predicate
		return (_Wait_until1(_Lck, _Abs_time, _Pred));
		}

	_NODISCARD native_handle_type native_handle()
		{	// return condition variable handle
		return (_Mycnd());
		}

	void _Register(unique_lock<mutex>& _Lck, int *_Ready)
		{	// register this object for release at thread exit
		_Cnd_register_at_thread_exit(_Mycnd(),
			_Lck.release()->_Mymtx(), _Ready);
		}

	void _Unregister(mutex& _Mtx)
		{	// unregister this object for release at thread exit
		_Cnd_unregister_at_thread_exit(_Mtx._Mymtx());
		}

private:
	aligned_storage_t<_Cnd_internal_imp_size,
		_Cnd_internal_imp_alignment> _Cnd_storage;

	_Cnd_t _Mycnd() noexcept
		{	// get pointer to _Cnd_internal_imp_t inside _Cnd_storage
		return (reinterpret_cast<_Cnd_t>(&_Cnd_storage));
		}

	template<class _Predicate>
		bool _Wait_until1(
			unique_lock<mutex>& _Lck,
			const xtime *_Abs_time,
			_Predicate& _Pred)
		{	// wait for signal with timeout and check predicate
		while (!_Pred())
			if (wait_until(_Lck, _Abs_time) == cv_status::timeout)
				return (_Pred());
		return (true);
		}
	};

在源码里面我们就可以看到,拷贝构造函数被删除,不允许使用,只有默认的构造函数,notify_one和notify_all,一个是唤醒一个线程,一个是唤醒所有线程,wait等待unique_lock兑现释放锁,这里会阻塞,而如果被其他地方调用了notify_all,也会唤醒,所有要注意判断,不是wait之后就解锁了;其他详细内容可以自行研究,下面就是例子:

#include 
#include 
#include 
#include 
#include 

std::deque<int> q;
std::mutex mu;
std::condition_variable cond;

void function_1() {
    int count = 10;
    while (count > 0) {
        std::unique_lock<std::mutex> locker(mu);
        q.push_front(count);
        locker.unlock();
        cond.notify_one();
        std::this_thread::sleep_for(std::chrono::seconds(1));
        count--;
    }
}

void function_2() {
    int data = 0;
    while ( data != 1) {
        std::unique_lock<std::mutex> locker(mu);
        while(q.empty()){
            cond.wait(locker);//等待notify_one
        }
        data = q.back();
        q.pop_back();
        locker.unlock();
        printf("function1 push_front a data to function2: %d \n", data);
    }
}
int main() {
    std::thread t1(function_1);
    std::thread t2(function_2);
    t1.join();
    t2.join();
    return 0;
}

编译加结果:

qilimi@qilimi-desktop:~/test$ vim condition_variable_test.cpp
qilimi@qilimi-desktop:~/test$ g++ -o test4 condition_variable_test.cpp -std=c++11 -lpthread
qilimi@qilimi-desktop:~/test$ ./test4
function1 push_front a data to function2: 10
function1 push_front a data to function2: 9
function1 push_front a data to function2: 8
function1 push_front a data to function2: 7
function1 push_front a data to function2: 6
function1 push_front a data to function2: 5
function1 push_front a data to function2: 4
function1 push_front a data to function2: 3
function1 push_front a data to function2: 2
function1 push_front a data to function2: 1
qilimi@qilimi-desktop:~/test$

条件变量就到这里了。

你可能感兴趣的:(程序基础,linux,多线程)