1. 程式人生 > >c++11 std::async使用注意

c++11 std::async使用注意

    std::async,c++11中提供的非同步任務高階抽象,包含在 <future>標頭檔案中,能讓你方便的實現非同步地執行一個任務

並在需要地時候獲取其結果。和直接使用std::thread 相比,有著一些優勢:

  1.std::async 返回的future物件,可以方便地等待callable物件執行完成並獲取其返回值

  2.能從實現庫的一些高階功能中獲益,比如執行緒池等,並大大減少異常的產生。

當然其使用更簡潔。但是async存在著一些問題,使用的時候要小心。

1.async的啟動策略,兩種啟動策略 std::launch::async , std::launch::deferred

如果不指定,將採用預設的啟動策略,等同於 launch::async | launch::deferred

並且標準中沒有規定預設採用哪種策略,有實現庫自己決定,測試VS2013中預設方式為async而gcc4.8.2為deferred。

在使用時就要注意。如果為launch::deferred模式,那麼永遠不會啟動新執行緒去執行,直到你呼叫 wait 或 get時

才在你的當前執行緒中去執行callable物件。如果你需要讓callable立即啟動執行緒去執行,那麼一定要

在呼叫async時指定launch::async,可以使用一個簡單的包裝避免忘記:

template <typename F, typename... Args>
inline auto real_async(F&& f, Args&&... args)
  -> std::future<typename std::result_of<F(Args...)>::type>
{
    return std::async(std::launch::async, 
		      std::forward<F>(f), 
		      std::forward<Args>(args)...);
}

2.std::async返回的future物件

以下程式碼存在一個問題

void work_proc()
{
    vector<int> numbers;
    ...   //do something
    real_async(some_proc,  std::move(numbers));
}

在當前執行緒中呼叫了work_proc,希望讓單獨的一個執行緒去處理一些不關心結果的耗時的事情,然後

當前執行緒再去處理一些其他的任務。然而事情不一定如你期待那樣。實際上在vs2013上一切表現如

預期,在gcc4.8.2測試,會一直阻塞在work_proc中,直到some_proc執行完成。

在標準庫中有說明,在std::future的解構函式中:

~future(); (since C++11)

Releases any shared state. This means

  • if the return object or provider holds the last reference to its shared state, the shared state is destroyed; and
  • the return object or provider gives up its reference to its shared state; and
  • these actions will not block for the shared state to become ready, except that it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state.

注意第三條:在以下三個條件全部滿足時future析構可能阻塞

(1)共享狀態是由呼叫std::async時建立的

(2)共享狀態還不是ready狀態

(3)被析構的當前物件持有著對共享狀態的最後一個引用

而work_proc函式中的情況剛好全部滿足。然而不幸的是如果你仍然想用async,並且忽略其返回,是沒辦法避免阻塞的。

但是還是有辦法實現相同的目的,使用std::packaged_task和std::thread.

template <typename F, typename... Args>
auto really_async2(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
    using _Ret = typename std::result_of<F(Args...)>::type;
    auto _func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    std::packaged_task<_Ret()> tsk(std::move(_func));
    auto _fut = tsk.get_future();
    std::thread thd(std::move(tsk));
    thd.detach();
    return _fut;
}