簡體   English   中英

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()此替代實現 ,它不會導致返回的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!
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM