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

bwe*_*ber 3 c++ linux mutex deadlock asynchronous

我刚编译了一个我在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();
}
Run Code Online (Sandbox Code Playgroud)

这是在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它试图锁定互斥锁之后的点在Linux下永远不会达到.

现在,问题是什么?是我的代码有问题,还是有什么特别的东西我必须在Linux上注意?(编译器标志等)对我来说,这似乎是异步调用是同步的,从而导致死锁.但为什么会这样呢

And*_*owl 5

这个电话:

async(std::launch::async, &BorderExtractor::preprocessImage, this, _cachedSettings);
Run Code Online (Sandbox Code Playgroud)

有效地同步运行.这是因为std::future返回的析构函数std::async()最终会与异步计算结合 - 请注意,如果以其他方式获得未来,行为会有所不同.

由于你没有保留std::asyncalive 返回的未来对象,它的生命周期在函数调用返回后立即结束,并且它的析构函数阻塞直到异步计算终止 - 这是永远的,因为这似乎导致死锁.

之所以在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;   
}
Run Code Online (Sandbox Code Playgroud)

作为旁注,请避免显式锁定/解锁互斥锁.相反,使用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!
}
Run Code Online (Sandbox Code Playgroud)