C++:std::async 和 std::mutex 在 Linux 上导致死锁但在 Windows 上运行?

标签 c++ linux asynchronous mutex deadlock

我刚刚为 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下永不返回的函数。它一直运行到异步调用,然后才停止。它几乎看起来好像函数不是异步启动的,程序等待它返回,这是行不通的,因为另一个函数将尝试锁定同一个互斥量。

这是异步调用的函数:

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 的析构函数最终加入了异步计算 - 注意,行为如果你以其他方式获得 future ,那就不一样了。

因为你没有让 std::async 返回的 future 对象保持事件状态,它的生命周期在函数调用返回后立即结束,并且它的析构函数阻塞直到异步计算终止 - 这是永远的,因为这似乎会导致僵局。

这在 Windows 上工作的原因可能是因为您使用的是标准库的不兼容实现(例如 VS2013 附带的 Microsoft 实现),其中 future 的析构函数不加入异步计算 - MS 有意这样做,遵循 this (rejected) proposal by Herb Sutter 中说明的基本原理.

如果您正在寻找一种即发即弃的方法,请考虑 this alternative implementation of std::async() ,这不会导致返回的 future 阻止销毁(由 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!
}

关于C++:std::async 和 std::mutex 在 Linux 上导致死锁但在 Windows 上运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27991830/

相关文章:

c++ - 二叉搜索树问题

c++ - 在字典中查找单词模式,高性能

c++ - 如果 bug 没有持续明显地出现,如何查看它是否仍然存在?

linux - 如何在 bash 脚本中对 while 循环的第一次迭代执行特定操作

javascript - 保留 $http.get 异步结果处理的变量值

c++ - doxygen 方程显示不正确

linux - 如何在打开新终端时自动登录到ssh服务器

python - PyCharm 项目文件消失了

python - Asyncio执行流程问题

c# - 我可以利用哪个 Task<T> 扩展来递归等待其他任务?