admin管理员组文章数量:1599528
一、condition_variable条件变量简介
当std::condition_variable对象的某个wait函数被调用的时候,它使用std::unique_lock(通过std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的std::condition_variable对象上调用了notification函数来唤醒当前线程。
condition_variable成员函数:
- condition_variable: 不可拷贝不可赋值;
- notify_one():唤醒一个等待的线程;
- notify_all():唤醒所有等待的线程;
- wait():阻塞等待直到被唤醒;
- wait_for():阻塞等待被唤醒,或者超时;
- wait_until():阻塞等待被唤醒,或者到某个时间点。
二、demo
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using std::mutex;
using std::condition_variable;
using std::unique_lock;
using std::thread;
using std::cout;
using std::endl;
mutex mtx;// 全局互斥锁
condition_variable cv;// 全局条件变量
bool ready = false;// 全局标志位
void do_print_id(int id)
{
/**********************************************************
*我们发现,在条件变量cv的wait函数中,我们传入了一个lock
*参数,为什么要用锁呢?因为ready为临界变量,主线程中会
*“写”它,子线程中要“读”它,这样就产生了数据竞争,并且这
*个竞争很危险。
*假如现在执行完条件判断后,时间片轮转,该子线程暂停执行。
*而恰好在这时,主线程修改了条件,并调用了cv.notify_all()
*函数。这种情况下,该子线程是收不到通知的,因为它还没挂起。
*等下一次调度子线程时,子线程接着执行2将自己挂起。
*但现在主线程中的notify早已经调用过了,不会再调第二次了,
*所以该子线程永远也无法唤醒了。为了解决上面的情况,就要使
*用某种同步手段来给线程加锁。而c++11的condition_variable
*选择了用unique_lock<Mutex>来配合完成这个功能。并且我们只
*需要加锁,条件变量在挂起线程时,会调用原子操作来解锁。
**********************************************************/
unique_lock<mutex> lck(mtx);
while (!ready)// 如果标志位不为 true, 则等待...
{
// 当前线程被阻塞。
cv.wait(lck);
}
// 如果线程被唤醒, 则继续往下执行打印线程编号id。
cout << "thread " << id << endl;
}
void go()
{
unique_lock<mutex> lck(mtx);
ready = true;// 设置全局标志位为 true。
cv.notify_all();// 唤醒所有线程。
}
int main()
{
thread threads[10];
for (int i = 0; i < 10; ++i)
{
threads[i] = thread(do_print_id, i);//所有线程都被挂起。
}
cout << "10 threads ready to race...\n";
go();//唤醒所有线程。
for (auto &th : threads)
{
th.join();
}
system("pause");
return EXIT_SUCCESS;
}
三、同步队列的实现
/***********************************************************************
*同步队列要求只能有一个任务对其进行操作(因此无需再对其使用同步操作,同
步队列内部是同步的)。当队列是空时,会导致取该队列的线程阻塞;当队列满
(设置固定大小的队列)时,会导致写该队列的线程阻塞。
************************************************************************/
#include <mutex>
#include <thread>
#include <condition_variable>
#include <chrono>
#include <iostream>
#include <list>
#include <vector>
#include <memory>
using namespace std;
template<typename T>
class SynQueue
{
public:
//构造函数
SynQueue(int MaxSize) : m_maxsize(MaxSize) { }
~SynQueue() { }
//将T类型对象放入队列
void Put(const T &x)
{
unique_lock<mutex> lck(m_mutex);
while(isFull())
{
m_notFull.wait(lck);
}
m_queue.push_back(x);
//通过条件变量唤醒一个线程,也可以所有线程。
m_notEmpty.notify_one();
}
//将T类型对象从队列取出
void Take(T &x)
{
unique_lock<mutex> lck(m_mutex);
while(isEmpty())
{
std::cout << "no resource... please wait" << std::endl;
m_notEmpty.wait(lck);
}
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();
}
//判断队列是否为空
bool Empty()
{
unique_lock<mutex> lck(m_mutex);
return m_queue.empty();
}
//判断队列是否为满
bool Full()
{
unique_lock<mutex> lck(m_mutex);
return m_queue.size() == m_maxsize;
}
//返回队列大小
size_t Size()
{
unique_lock<mutex> lck(m_mutex);
return m_queue.size();
}
private:
//判断空或满,内部使用不需要加锁。
inline bool isFull() const
{
return m_queue.size() == m_maxsize;
}
inline bool isEmpty() const
{
return m_queue.empty();
}
//队列
list<T> m_queue;
//互斥锁
mutex m_mutex;
//不为空时的条件变量
condition_variable m_notEmpty;
//不为满时的条件变量
condition_variable m_notFull;
//队列最大长度
int m_maxsize;
};
void func(SynQueue<int> *sq)
{
int ret;
sq->Take(ret);
cout << ret << endl;
}
int main()
{
SynQueue<int> syn(20);
for(int i = 0; i < 10; ++i)
{
syn.Put(i);
}
cout << syn.Size() << endl;
vector<shared_ptr<thread>> tvec;
for(int i = 0; i < 11; ++i)
{
//创建线程并且将管理线程的智能指针保存到容器中
tvec.push_back(make_shared<thread>(func, &syn));
//变为后台线程
tvec[i]->detach();
}
std::this_thread::sleep_for(std::chrono::seconds(10));
//添加一个资源
syn.Put(11);
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
本文标签: 变量条件conditionvariable
版权声明:本文标题:条件变量-condition_variable 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728323571a1154144.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论