先上代码:
// condition_variable example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <vector>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
std::vector<int> vec;
void print_id(int id) {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck,[]{return ready;});
std::cout << "thread " << id << '\n';
for(int i = 0; i < 1000; ++i){
std::this_thread::sleep_for(std::chrono::microseconds(10));
vec.push_back(id+i);
}
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all(); // 这是重点
}
int main()
{
std::thread threads[10];
// spawn 10 threads:
for (int i = 0; i < 10; ++i)
threads[i] = std::thread(print_id, i*1000);
std::cout << "10 threads ready to race...\n";
go();
for (auto& th : threads) {
th.join();
}
std::this_thread::sleep_for(std::chrono::microseconds(2000));
std::cout<<"vec size: "<<vec.size()<<std::endl;
return 0;
}
我是在网上解释notify_one和notify_all区别的代码(例如:notify_one和notify_all的区别)中添加了vector用来并发插入数据。
notify_one的输出:
@"10 threads ready to race...\r\n"
@"thread 0\r\n"
notify_all的输出:
@"10 threads ready to race...\r\n"
@"thread 6000\r\n"
@"thread 4000\r\n"
@"thread 5000\r\n"
@"thread 0\r\n"
@"thread 3000\r\n"
@"thread 2000\r\n"
@"thread 7000\r\n"
@"thread 1000\r\n"
@"thread 8000\r\n"
@"thread 9000\r\n"
@"vec size: 10000\r\n"
可以看到,在cv notify_all之后,所有的线程都往vecotr中push了数据,这里是如何保证同步的呢,看一下wait的源码:
struct __lock_external
{
template <class _Lock>
void operator()(_Lock* __m) {__m->lock();}
};
template <class _Lock>
void
condition_variable_any::wait(_Lock& __lock)
{
shared_ptr<mutex> __mut = __mut_;
unique_lock<mutex> __lk(*__mut);
__lock.unlock();//unlock mutex,进入等待。
unique_ptr<_Lock, __lock_external> __lxx(&__lock);//__lock_external作为deleter传入到unique_ptr中,析构的时候__lock.lock()
lock_guard<unique_lock<mutex> > __lx(__lk, adopt_lock);
__cv_.wait(__lk);
} // 析构的说话__mut_.unlock(), __lock.lock()
在cv.wait()结束的时候mtx.lock() ,print_id()结束后mtx.unlock(),这个时候10个线程中的vec是保持同步状态。
总结:
1、所有的等待某个cv的线程都必须使用相同的mutex。
2、当wait ()家族函数被调用时,mutex必须被unique_lock锁定。
3、所有通知都会被自动同步化,并发调用notify_one和notify_all没有问题。