前言
一次,我封装了一个互斥锁,然后,打算写个程序测试一下。于是写了大概如下代码:
#include <memory>
#include <stdio.h>
#include <future>
#include <chrono>
#include <semaphore.h>
#include <exception>
class Mutex {
public:
Mutex() {
if (sem_init(&sem_, 0, 0) != 0)
throw std::logic_error("sem_init");
}
~Mutex() {
sem_destroy(&sem_);
}
void lock() {
if (sem_post(&sem_) != 0)
throw std::logic_error("sem_post");
}
void unlock() {
if (sem_wait(&sem_) != 0)
throw std::logic_error("sem_post");
}
private:
Mutex(const Mutex&) = delete;
Mutex(const Mutex&&) = delete;
Mutex& operator=(const Mutex&) = delete;
Mutex& operator=(const Mutex&&) = delete;
sem_t sem_;
};
int main() {
Mutex mtx;
std::async([&mtx] {
mtx.unlock();
});
std::async([&mtx] {
mtx.lock();
});
}
乍一看,应该没啥问题,可是,为啥卡死了,难道死锁了?
解决过程
把 async 换成 thread,可以正常运行。问题来了,async,这个被推荐使用的函数,它咋了
后来,我看到了这段话
若从 std::async 获得的 std::future 未被移动或绑定到引用,则在完整表达式结尾, std::future 的析构函数将阻塞直至异步计算完成,实质上令如下代码同步:
std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f()
std::async(std::launch::async, []{ g(); }); // f() 完成前不开始
对应上面的代码,也就是说,加锁那块会等待解锁的完成,才能继续执行下去,这就形成了死锁,如果改成这样就没问题了:
auto a = std::async([&mtx] {
mtx.unlock();
});
std::async([&mtx] {
mtx.lock();
});
结论
以调用 std::async 的方式获得的 std::future 的析构函数会阻塞程序的执行
同理的代码为:
void func() {
auto a = std::async([]{
std::this_thread::sleep_for(std::chrono::seconds(1));
});
}
int main() {
func();
puts("hello");
return 0;
}