C++:std::async 和 std::mutex 会导致 Linux 上的死锁,但在 Windows 上运行
C++: std::async and std::mutex leads to deadlock on Linux but runs on Windows?
我刚刚编译了一个我一直在Windows for Linux下工作的项目,发现它在某个时候挂起了。由于我使用的是 std::async 和 std::mutex,我的第一个假设是,这可能是一个死锁问题。但是,我想知道为什么它在Windows上运行良好。这是代码:
void BorderExtractor::preprocessImageAsync(const PreprocessingSettings& settings) {
_preprocessingMutex.lock();
if (!_preprocessingActive) {
_preprocessingActive = true;
std::async(std::launch::async, &BorderExtractor::preprocessImage, this, settings);
//this point is never reached on linux
_preprocessingUpToDate = true;
} else {
_cachedSettings = settings;
_preprocessingUpToDate = false;
}
_preprocessingMutex.unlock();
}
这是在 Linux 下永远不会返回的函数。它一直运行到异步调用,然后停止。看起来好像函数没有异步启动,程序等待它返回,这不起作用,因为另一个函数将尝试锁定相同的互斥锁。
下面是异步调用的函数:
void BorderExtractor::preprocessImage(PreprocessingSettings settings) {
//here some image processing stuff is done
_preprocessingMutex.lock();
//this point is never reached on linux
if (!_preprocessingUpToDate) {
_preprocessingUpToDate = true;
_preprocessingMutex.unlock();
std::async(std::launch::async, &BorderExtractor::preprocessImage, this, _cachedSettings);
} else {
_preprocessingUpToDate = true;
_preprocessingActive = false;
_preprocessingMutex.unlock();
}
}
在 Linux 下,它尝试锁定互斥锁后的点永远不会达到。
现在,问题出在哪里?是我的代码出了问题,还是在 Linux 上我必须注意什么特别的事情?(编译器标志等)对我来说,这似乎好像异步调用是同步的,因此会导致死锁。但为什么要这样
此调用:
async(std::launch::async, &BorderExtractor::preprocessImage, this, _cachedSettings);
有效地同步运行。这是因为std::async()
返回的std::future
的析构函数最终与异步计算联接 - 请注意,如果您以其他方式获得未来,则行为会有所不同。
由于您没有使 std::async
返回的未来对象保持活动状态,因此它的生存期在函数调用返回后立即结束,并且其析构函数会阻塞,直到异步计算终止 - 这是永远的,因为这似乎会导致死锁。
这在Windows上工作的原因可能是由于您使用的标准库的不合规实现(例如VS2013附带的Microsoft实现),其中未来的析构函数不与异步计算连接 - MS故意这样做,遵循Herb Sutter的这个(被拒绝的)提案中说明的基本原理。
如果您正在寻找一种即发即弃的方法,请考虑 std::async()
的替代实现,它不会导致返回的未来在销毁时阻塞(由 bamboon 提供):
template<class Function, class... Args>
std::future<typename std::result_of<Function(Args...)>::type> async(
Function&& f,
Args&&... args)
{
using R = typename std::result_of<Function(Args...)>::type;
auto bound_task = std::bind(std::forward<Function>(f), std::forward<Args>(args)...);
auto task = std::packaged_task<R()>{std::move(bound_task)};
auto ret = task.get_future();
auto t = std::thread{std::move(task)};
t.detach();
return ret;
}
作为旁注,请避免显式锁定/解锁互斥锁。相反,请使用 RAII 包装器(如 std::lock_guard
或(如有必要)std::unique_lock
来确保您的互斥锁将被解锁,即使抛出异常或提前返回:
// The mutex will be unlocked automatically when the function returns.
std::lock_guard<std::mutex> lock{_preprocessingMutex};
if (!_preprocessingUpToDate) {
_preprocessingUpToDate = true;
async(std::launch::async, &BorderExtractor::preprocessImage, this, _cachedSettings);
// No need to call unlock() on the mutex!
} else {
_preprocessingUpToDate = true;
_preprocessingActive = false;
// No need to call unlock() on the mutex!
}
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 如果有标准::屏障,为什么是 std::闩锁?
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 返回持有 std::mutex 锁的 RAII 容器类
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 线程安全std::cout的死锁
- C++:std::async 和 std::mutex 会导致 Linux 上的死锁,但在 Windows 上运行
- 当从DllMain()锁定时,Visual Studio 2012死锁中的C++11 std::mutex