C++:std::async 和 std::mutex 在 Linux 上导致死锁但在 Windows 上运行?
C++: std::async and std::mutex leads to deadlock on Linux but runs on Windows?
我刚刚为 Linux 编译了一个我一直在 Windows 下工作的项目,发现它在某个点挂起。由于我使用 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 下永远不会 return 的功能。它一直运行到异步调用,然后才停止。它几乎看起来好像函数不是异步启动的,程序等待它到 return,这是行不通的,因为另一个函数将尝试锁定相同的互斥量。
下面是异步调用的函数:
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
return 的析构函数最终加入了异步计算 - 注意,如果您在其他方法。
由于您没有让 return 由 std::async
编辑的未来对象保持活动状态,它的生命周期在函数调用 return 之后立即结束,并且它的析构函数阻塞直到异步计算终止 - 这是永远的,因为这似乎会导致死锁。
这在 Windows 上起作用的原因可能是因为您使用的是标准库的不兼容实现(例如 VS2013 附带的 Microsoft 实现),其中 future 的析构函数不加入异步计算 - MS 有意这样做,遵循 this (rejected) proposal by Herb Sutter.
中说明的基本原理
如果您正在寻找一种即发即弃的方法,请考虑 this alternative implementation of std::async()
, which does not cause the returned future to block on destruction (courtesy of 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;
}
作为旁注,请明确避免 locking/unlocking 互斥锁。相反,使用 RAII 包装器,如 std::lock_guard
或(如有必要)std::unique_lock
以确保即使抛出异常或早期 return:
// 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!
}
我刚刚为 Linux 编译了一个我一直在 Windows 下工作的项目,发现它在某个点挂起。由于我使用 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 下永远不会 return 的功能。它一直运行到异步调用,然后才停止。它几乎看起来好像函数不是异步启动的,程序等待它到 return,这是行不通的,因为另一个函数将尝试锁定相同的互斥量。
下面是异步调用的函数:
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
return 的析构函数最终加入了异步计算 - 注意,如果您在其他方法。
由于您没有让 return 由 std::async
编辑的未来对象保持活动状态,它的生命周期在函数调用 return 之后立即结束,并且它的析构函数阻塞直到异步计算终止 - 这是永远的,因为这似乎会导致死锁。
这在 Windows 上起作用的原因可能是因为您使用的是标准库的不兼容实现(例如 VS2013 附带的 Microsoft 实现),其中 future 的析构函数不加入异步计算 - MS 有意这样做,遵循 this (rejected) proposal by Herb Sutter.
中说明的基本原理如果您正在寻找一种即发即弃的方法,请考虑 this alternative implementation of std::async()
, which does not cause the returned future to block on destruction (courtesy of 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;
}
作为旁注,请明确避免 locking/unlocking 互斥锁。相反,使用 RAII 包装器,如 std::lock_guard
或(如有必要)std::unique_lock
以确保即使抛出异常或早期 return:
// 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!
}