简体   繁体   English

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

[英]C++: std::async and std::mutex leads to deadlock on Linux but runs on Windows?

I just compiled a project I've been working on under Windows for Linux and found that it hangs at a certain point. 我刚刚编译了一个我一直在Windows for Linux下工作的项目,发现它在某个时候挂了。 Since I am using std::async and std::mutex my first assumption was, that it could be a deadlock problem. 由于我使用的是std :: async和std :: mutex,因此我的第一个假设是,这可能是一个死锁问题。 However, then I wonder why it runs fine on Windows. 但是,我不知道为什么它在Windows上可以正常运行。 Here is the code: 这是代码:

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();
}

This is the function that never returns under Linux. 这是在Linux下永远不会返回的函数。 It runs until the async call and then it just stops. 它一直运行到异步调用,然后才停止。 It nearly appears as if the function wasn't launched asynchronously and the program waits for it to return, what wouldn't work, because the other function will try to lock the same mutex. 几乎好像该函数不是异步启动的,程序等待它返回,这是行不通的,因为另一个函数将尝试锁定相同的互斥锁。

Here is the function that's called asynchronously: 这是异步调用的函数:

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();
    }
}

The point after it tries to lock the mutex is never reached under linux. 尝试锁定互斥锁之后的点在Linux下永远不会达到。

Now, what is the problem? 现在,这是什么问题? Is it my code that is faulty, or is there something special I have to pay attention to on Linux? 是我的代码有错误,还是在Linux上我必须注意一些特别的事情? (compiler flags, etc) To me this appears as if the async call is synchronous and thus causes a deadlock. (编译器标志等)对我来说,这似乎是异步调用是同步的,从而导致死锁。 But why should that be 但是为什么要这样

This call: 这个电话:

async(std::launch::async, &BorderExtractor::preprocessImage, this, _cachedSettings);

Effectively runs synchronously. 有效地同步运行。 This is due to the fact that the destructor of an std::future returned by std::async() ends up joining with the asynchronous computation - notice, that the behavior would be different if you obtained the future in other ways. 这是由于以下事实: std::async()返回的std::future的析构函数最终加入了异步计算-请注意,如果您以其他方式获得未来,则行为会有所不同。

Since you are not keeping the future object returned by std::async alive, its lifetime ends immediately after the function call returns, and its destructor blocks until the asynchronous computation terminates - which is forever, as this seems to cause a deadlock. 由于您没有让std::async返回的未来对象保持活动状态,因此其生存期在函数调用返回后立即结束,并且其析构函数将阻塞直到异步计算终止-这是永远的,因为这似乎会导致死锁。

The reason why this works on Windows may be due to the fact that you are using a non-compliant implementation of the Standard Library (eg Microsoft's implementation that comes with VS2013), in which the future's destructor does not join with the asynchronous computation - MS did this intentionally, following the rationale illustrated in this (rejected) proposal by Herb Sutter . 之所以可以在Windows上运行,可能是由于您使用的是标准库的不兼容实现(例如VS2013附带的Microsoft实现),其中将来的析构函数不与异步计算结合-MS遵循Herb Sutter的(被拒绝)提案中说明的基本原理,有意这样做。

If you are looking for a fire-and-forget approach, consider this alternative implementation of std::async() , which does not cause the returned future to block on destruction (courtesy of bamboon ): 如果您正在寻找一劳永逸的方法,请考虑使用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;   
}

As a side note, avoid explicitly locking/unlocking mutexes. 另外,请避免显式锁定/解锁互斥锁。 Rather, use RAII wrappers like std::lock_guard or (if necessary) std::unique_lock to make sure your mutex will be unlocked even if an exception is thrown or in case of an early return: 而是使用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!
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM