前面的章节中,线程使用互斥变量同步访问普通资源,而本章将讨论另外一种同步问题。
案例代码:
#include<iostream> #include<string> #include<list> #include<thread> #include<functional> #include<mutex>
using namespace std;
扫描二维码关注公众号,回复:
3180195 查看本文章
std::list<int> q;
std::mutex mu;
void function1() { int count = 10; while (count > 0) { std::unique_lock<mutex>locker(mu); q.push_back(count); locker.unlock(); std::this_thread::sleep_for(chrono::seconds(1)); count--; }
}
void function2() { int data = 0; while (data != 1) { std::unique_lock<mutex> locker(mu); if (!q.empty()) { data = q.back(); q.pop_back(); locker.unlock(); cout << "t2 data fomr t1:" << data << endl; } else locker.unlock(); } }
int main() { std::thread t1(function1); std::thread t2(function2); t1.join(); t2.join(); return 0; } |
从这段代码中可以看出来,list q 是一个被function1和function2共享的序列,,因此在q.push_back和function2中的q.pop_bak()之前都需要加锁。
但是在function2函数中,由于if… else…的使用,使得function2处于q.empty()的循环之中——如果不是进行pop操作,如果是则进行解锁操作,从而形成一种循环状态,导致程序运行低效。
解决方案一种是在else中的unlock()下面添加如下语句:
Std::this_thread::sleep_for(chrono::milliseconds(10));
但其不足之处在于等待的时间不容易控制。
另外一种解决方案则是本章的章节名称——条件变量。
Cond.wait(locker)可以将线程function2休眠,等到function1后notify_one()/all()通知后才向下进行。而使用wait中是locker的原因在于,使用互斥对象mu后,被线程function2锁住,一个被锁住的线程不会在被锁住的情况下休眠,使用wait(locker)之后,可以解锁互斥对象mu让其进行休眠,而后加锁。而q.pop_bak()后再解锁。(有问题)
由于线程function2中涉及到加/解锁,因此只能使用unique_lock<>,而不能使用lock_guard<>.
同时,线程function2可能被自己激活——称为伪激活,碰到伪激活,但不需要工作,这时需要使用lambda表达式
在wait()中添加另外的参数:
Cond.wait(locker,[](){return !a.empty();}); |
清单如下
#include<iostream> #include<string> #include<list> #include<thread> #include<functional> #include<mutex> #include<condition_variable>
using namespace std;
std::list<int> q; std::mutex mu; std::condition_variable cond;
void function1() { int count = 10; while (count > 0) { std::unique_lock<mutex>locker(mu);
q.push_back(count); locker.unlock(); cond.notify_all(); //需要多个线程激活时使用notify_all(),如果有一个时,使用notify_one()即可 std::this_thread::sleep_for(chrono::seconds(1)); count--; }
}
void function2() { int data = 0; while (data != 1) { std::unique_lock<mutex> locker(mu); cond.wait(locker, []() {return !q.empty(); }); //防止"伪激活"的出现 data = q.back(); q.pop_back(); locker.unlock(); cout << "t2 data fomr t1:" << data << endl;
} }
int main() { std::thread t1(function1); std::thread t2(function2); t1.join(); t2.join(); return 0; } |
柏树 于 2016/8/17 星期三 8:43 修改