~~~~ 刚刚本来想写一下信号量,同时看一下标准库里面的信号量代码,但是却突然发现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$
条件变量就到这里了。