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 mtx
cv.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 mtx
cv.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();
}
};