首先,举个例子:在应用程序中有4个进程thread1,thread2,thread3和thread4,有一个int类型的全局变量iCount。iCount初始化为0,thread1和thread2的功能是对iCount的加1,thread3的功能是对iCount的值减1,而thread4的功能是当iCount的值大于等于100时,打印提示信息并重置iCount=0。
互斥量代码:
thread1/2:
while (1)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
}
thread4:
while(1)
{
pthead_mutex_lock(&mutex);
if (100 <= iCount)
{
printf("iCount >= 100\r\n");
iCount = 0;
pthread_mutex_unlock(&mutex);
}
else
{
pthread_mutex_unlock(&mutex);
}
}
在上面代码中由于thread4并不知道什么时候iCount会大于等于100,所以就会一直在循环判断,但是每次判断都要加锁、解锁(即使本次并没有修改iCount)。这就带来了问题一:CPU浪费严重。所以在代码中添加了sleep(),这样让每次判断都休眠一定时间。但这又带来的第二个问题:如果sleep()的时间比较长,导致thread4处理不够及时,等iCount到了很大的值时才重置。对于上面的两个问题,可以使用条件变量来解决。
条件变量代码:
thread1/2:
while(1)
{
pthread_mutex_lock(&mutex);
iCount++;
pthread_mutex_unlock(&mutex);
if (iCount >= 100)
{
pthread_cond_signal(&cond);
}
}
thread4:
while (1)
{
pthread_mutex_lock(&mutex);
while(iCount < 100)
{
pthread_cond_wait(&cond, &mutex);
}
printf("iCount >= 100\r\n");
iCount = 0;
pthread_mutex_unlock(&mutex);
}
pthread_cond_signal放在lock,unlock之间和之后都可以:
1. 在之间的情况下,可能会把等待线程饿死,原因是signal信号发出时未unlock,所有等待线程继续阻塞在wait
2. 在之后的情况下,可能会在signal信号发出后,有其他线程修改了条件变量,情况同上一节的第三点问题
c++11的条件变量类的定义:
class condition_variable {
public:
typedef _Cnd_t native_handle_type;
condition_variable() { // 构造函数,初始化条件变量,所有的条件变量必须初始化后才能使用。
_Cnd_initX(&_Cnd);
}
~condition_variable() _NOEXCEPT { // 析构函数
_Cnd_destroy(&_Cnd);
}
condition_variable(const condition_variable&) = delete;
condition_variable& operator=(const condition_variable&) = delete;
void notify_one() _NOEXCEPT { // 唤醒一个在等待线程
_Cnd_signalX(&_Cnd);
}
void notify_all() _NOEXCEPT { // 唤醒所有在等待的线程
_Cnd_broadcastX(&_Cnd);
}
void wait(unique_lock& _Lck) { // 等待
_Cnd_waitX(&_Cnd, &_Lck.mutex()->_Mtx);
}
template<class _Predicate>
void wait(unique_lock& _Lck, _Predicate _Pred) { // 等待,带有描述式
while (!_Pred())
wait(_Lck);
}
template<class _Rep, class _Period>
_Cv_status wait_for(unique_lock& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time) {
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& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time, _Predicate _Pred) {
stdext::threads::xtime _Tgt = _To_xtime(_Rel_time);
return (wait_until(_Lck, &_Tgt, _Pred));
}
template<class _Clock, class _Duration>
_Cv_status wait_until( unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time) {
typename chrono::time_point<_Clock, _Duration>::duration
_Rel_time = _Abs_time - _Clock::now();
return (wait_for(_Lck, _Rel_time));
}
template<class _Clock, class _Duration, class _Predicate>
bool wait_until(unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) {
typename chrono::time_point<_Clock, _Duration>::duration
_Rel_time = _Abs_time - _Clock::now();
return (wait_for(_Lck, _Rel_time, _Pred));
}
_Cv_status wait_until( unique_lock& _Lck, const xtime *_Abs_time) {
if (!_Mtx_current_owns(&_Lck.mutex()->_Mtx))
_Throw_Cpp_error(_OPERATION_NOT_PERMITTED);
int _Res = _Cnd_timedwaitX(&_Cnd, &_Lck.mutex()->_Mtx, _Abs_time);
return (_Res == _Thrd_timedout ? cv_status::timeout : cv_status::no_timeout);
}
template<class _Predicate>
bool wait_until(unique_lock& _Lck, const xtime *_Abs_time, _Predicate _Pred) {
bool _Res = true;
while (_Res && !_Pred())
_Res = wait_until(_Lck, _Abs_time)
!= cv_status::timeout;
return (_Pred());
}
native_handle_type native_handle() { // 返回条件变量的句柄
return (_Cnd);
}
void _Register(unique_lock& _Lck, int *_Ready) {
_Cnd_register_at_thread_exit(&_Cnd, &_Lck.release()->_Mtx, _Ready);
}
void _Unregister(mutex& _Mtx) {
_Cnd_unregister_at_thread_exit(&_Mtx._Mtx);
}
private:
_Cnd_t _Cnd;
};
该类禁止了拷贝和赋值,将linux中的pthread进行了封装,具有以下几个类似的方法:
condition_variable::notify_one():唤醒一个处于等待状态的线程;
condition_variable::notify_all():唤醒所有处于等待状态的线程;
condition_variable::wait():将线程置于等待状态,直到被notify_xxx()唤醒;
condition_variable::wait_for():将线程置于等待状态,直到一段时间结束后自动醒来或被notify_xxx()唤醒;
condition_variable::wait_until():将线程置于等待状态,直到指定的时间点到来自动唤醒或被notify_xxx()唤醒;
std::cout
#include // std::thread
#include // std::mutex, std::unique_lock
#include // std::condition_variable
std::mutex mtx; // 全局互斥锁.
std::condition_variable cv; // 全局条件变量.
bool ready = false; // 全局标志位.
void do_print_id(int id)
{
std::unique_lock <std::mutex> lck(mtx);
while (!ready) // 如果标志位不为 true, 则等待...
cv.wait(lck); // 当前线程被阻塞, 当全局标志位变为 true 之后,
// 线程被唤醒, 继续往下执行打印线程编号id.
std::cout << "thread " << id << '\n';
}
void go()
{
std::unique_lock <std::mutex> lck(mtx);
ready = true; // 设置全局标志位为 true.
cv.notify_all(); // 唤醒所有线程.
}
int main()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(do_print_id, i);
std::cout << "10 threads ready to race...\n";
go(); // go!
for (auto & th:threads)
th.join();
return 0;
}