1114. 按序打印
我们提供了一个类:
public class Foo { public void first() { print(“first”); } public void second() { print(“second”); } public void third() { print(“third”); }
}
三个不同的线程 A、B、C 将会共用一个 Foo 实例。
一个将会调用 first() 方法
一个将会调用 second() 方法
还有一个将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。
互斥锁
互斥锁是用来防止多个线程同时访问共享资源对象的机制,在同一时间只有一个线程可以拥有一个特定的锁对象,其他线程如果尝试获取锁会阻塞直到锁资源被释放或直接返回失败。
针对这道题我们可以用两个互斥锁来阻塞 second 和 third 函数,分别在 first 和 second 执行结束后解锁。
mutex mtx1,mtx2;void first(function<void()> printFirst) {printFirst();mtx1.unlock();}void second(function<void()> printSecond) {mtx1.lock();printSecond();mtx1.unlock();mtx2.unlock();}void third(function<void()> printThird) {mtx2.lock();printThird();mtx2.unlock();}
注意:上述代码本质上是错误的!如下几点:
- mutex在加锁解锁过程中,其所有权必须在同一线程上!
由同一个线程来对一个
mutex对象进行lock和unlock操作
RAII机制优化
因为 mutex 对象本身是不保护任何数据的,我们只是通过 mutex 的机制来保护数据被同时访问,所以最好使用 lock_guard 或者 unique_lock 提供的 RAII 机制来管理 mutex 对象,而不是直接操作 mutex 对象;其中 lock_guard 只拥有构造和析构函数,用来实现 RAII 机制,而 unique_lock 是一个完整的 mutex 所有权包装器,封装了所有 mutex 的函数:
条件变量
std::condition_variable是一种用来同时阻塞多个线程的同步原语(synchronization primitive),std::condition_variable必须和std::unique_lock搭配使用:
class Foo {condition_variable cv;mutex mtx;int k = 0;public:void first(function<void()> printFirst) {printFirst();k = 1;cv.notify_all(); // 通知其他所有在等待唤醒队列中的线程}void second(function<void()> printSecond) {unique_lock<mutex> lock(mtx); // lock mtxcv.wait(lock, [this](){ return k == 1; }); // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 1 才能继续运行printSecond();k = 2;cv.notify_one(); // 随机通知一个(unspecified)在等待唤醒队列中的线程}void third(function<void()> printThird) {unique_lock<mutex> lock(mtx); // lock mtxcv.wait(lock, [this](){ return k == 2; }); // unlock mtx,并阻塞等待唤醒通知,需要满足 k == 2 才能继续运行printThird();}};
异步操作

class Foo {promise<void> pro1, pro2;public:void first(function<void()> printFirst) {printFirst();pro1.set_value();}void second(function<void()> printSecond) {pro1.get_future().wait();printSecond();pro2.set_value();}void third(function<void()> printThird) {pro2.get_future().wait();printThird();}};

class Foo {function<void()> task = []() {};packaged_task<void()> pt_1{ task }, pt_2{ task };public:void first(function<void()> printFirst) {printFirst();pt_1();}void second(function<void()> printSecond) {pt_1.get_future().wait();printSecond();pt_2();}void third(function<void()> printThird) {pt_2.get_future().wait();printThird();}};
原子操作
class Foo {std::atomic<bool> a{ false };std::atomic<bool> b{ false };public:void first(function<void()> printFirst) {printFirst();a = true;}void second(function<void()> printSecond) {while (!a)this_thread::sleep_for(chrono::milliseconds(1));printSecond();b = true;}void third(function<void()> printThird) {while (!b)this_thread::sleep_for(chrono::milliseconds(1));printThird();}};

