2013-07-31 68 views
43

我試圖深入探討新的C++ 11標準的所有選項,同時使用std :: async並閱讀其定義,我注意到2件事情,至少在linux下使用gcc 4.8.1:爲什麼要使用std :: async?

  • 這就是所謂的異步,但它得到了一個真正的「順序行爲」,在這裏你撥打未來與異步功能,程序塊,直到FOO執行相關的行基本上它已完成。
  • 它取決於與其他外部庫完全相同的外部庫,以及更好的非阻塞解決方案,這意味着pthread,如果要使用std::async,則需要pthread。

在這一點上,我很自然地問,爲什麼選擇std :: async而不是一組簡單的函子呢?這個解決方案甚至根本無法擴展規模,您呼叫的未來越多,程序響應也就越少。

我錯過了什麼嗎?你能否展示一個被授予以異步,非阻塞方式執行的示例?

+0

@rsaxvc你調用異步函數,例如'future.get()' – user2485710

+1

你的假設是錯誤的。 async()旨在提供一個同步點,以便您可以獲取異步評估函數的結果。 – DanielKO

+0

與其他選項相比,C++目前的「異步」概念並沒有真正將任何重要的東西(可移植性除外)帶到表中。一旦獲得持續完成支持(這是幾乎所有其他平臺稱爲「異步」的重要組成部分),我懷疑你會發現它有更多的用途。 –

回答

41

如果您需要異步操作的結果,那麼無論您使用什麼庫,您的都會阻止。這個想法是,你可以選擇什麼時候阻止,並且希望當你這樣做的時候,你阻止了一個微不足道的時間,因爲所有的工作已經完成了。

還請注意std::async可以使用政策std::launch::asyncstd::launch::deferred啓動。如果您沒有指定它,則可以選擇實現,並且可以選擇使用延遲評估,這會在您嘗試從未來獲得結果時導致所有工作都已完成,從而導致較長的塊。所以如果你想確保工作是異步完成的,請使用std::launch::async

+0

爲什麼需要「阻止」?爲什麼不假設一個異步函數需要儘快執行並且能夠檢索出結果呢?最重要的是,如果C++ 11提出了一個新的線程模型,爲什麼不在std的定義中使用它:: async? std :: async甚至不會授予使用多於1個線程的權限。在std :: launch ::下的AFAIK方法也只是未來的封裝,它和阻塞的執行概念是一樣的。 – user2485710

+1

@ user2485710當你檢索結果時,它需要阻塞,*如果*你需要啓動線程中的結果。如果結果未準備好,它不能使用結果。所以,如果你去得到結果,你必須等待,直到準備就緒。如果它已經準備好了,那麼阻塞時間將可以忽略不計。 – juanchopanza

+1

@ user2485710也注意到,根據我的經驗,'std :: async'具有異步行爲,如預期的那樣。 – juanchopanza

3

referencehttp://en.cppreference.com/w/cpp/thread/async

如果異步標誌設置(即政策&的std ::推出::異步!= 0),然後 異步執行在一個單獨的執行線程函數f彷彿 由std :: thread(f,args ...)產生,除了如果函數f 返回一個值或引發異常,它將存儲在可通過std :: future訪問的共享 狀態中,異步返回到 調用者

這是一個不錯的屬性來保持拋出異常的記錄。

+0

這不是一個保證,根據標準,我不明白爲什麼給定的C++ 11實現被迫使用不同於「通常」單線程執行的東西。 – user2485710

+2

@ user2485710它是強制性的,因爲標準是這樣說的:「就好像在一個新的執行線程中」(§30.6.8/ 3)。 –

+1

@ user2485710:和「好像在新的執行線程中」一樣具有可觀察的效果,例如線程本地人。當然,如果編譯器可以證明它不可觀察,那麼它可以在as-if規則下同步執行它,但這是一個質量實現問題。 – JohannesD

10

我認爲你的問題是std::future說它阻止get。如果結果尚未準備好,它只會阻止

如果您可以安排結果已經準備就緒,這不是問題。

有很多方法可以知道結果已經準備好。您可以輪詢future並詢問它(相對簡單),您可以使用鎖或原子數據來傳遞它已準備就緒的事實,您可以構建一個框架將「已完成」的項目傳遞到消費者可以與之交互的隊列中,你可以使用某種類型的信號(它只是一次阻止多個事物,或輪詢)。

或者,您可以完成您可以在本地完成的所有工作,然後阻止遠程工作。

作爲一個例子,設想一個並行遞歸合併排序。它將數組拆分爲兩個塊,然後在排序其他塊的同時對一個塊進行排序。一旦完成對其一半的排序,直到第二個任務完成後,始發線程才能進行。所以它做了.get()和塊。一旦完成了兩部分的排序,就可以進行合併(理論上,合併至少可以部分並行完成)。

這個任務的行爲像一個線性任務給那些在外面進行交互的人 - 當它完成時,數組被排序。

然後我們可以將其包裝在std::async任務中,並且有一個future排序數組。如果我們想要的話,我們可以添加一個信號程序,讓我們知道future已完成,但只有當我們有一個等待信號的線程時纔有意義。

56
  • 它被稱爲異步,但它得到了一個真正的「順序行爲」,

沒有,如果你使用的std::launch::async政策則異步在一個新的線程中運行。如果您未指定政策,則可能會在新線程中運行

基本上在你調用未來與你的異步函數foo相關聯的行中,程序會阻塞,直到foo執行完成。

只塊,如果富還沒有完成,但如果它是異步運行(例如,由於您使用std::launch::async政策),你需要它之​​前它可能已經完成。

  • 這取決於完全相同的外部庫等等,並且更好的,無阻塞的解決方案,這意味着並行線程,如果你想使用std ::異步你需要並行線程。

錯誤,它沒有使用P線程實現(在Windows上它不是,它使用ConcRT功能。)

在這一點上很自然的我問爲什麼選擇std :: async即使是一組簡單的函子?

因爲它保證線程安全並在線程間傳播異常。你能用一套簡單的仿函數來做到嗎?

這是一個解決方案,甚至根本沒有擴展規模,您呼叫的未來越多,程序響應也就越少。

不一定。如果您沒有指定啓動策略,那麼智能實現可以決定是否啓動新線程,或返回延遲函數,或返回稍後決定的事情,以便有更多資源可用時。

現在的確,在GCC的實現中,如果你沒有提供啓動策略,那麼對於當前版本,它將永遠不會運行在新線程中(對此有一個bugzilla report),但這是該實現的屬性,而不是一般的std::async。您不應該將標準中的規範與特定的實現混淆。閱讀一個標準庫的實現是學習C++ 11的一個不好的方法。

您能否展示一個被授予以異步,非阻塞方式執行的示例?

這不應該阻止:

auto fut = std::async(std::launch::async, doSomethingThatTakesTenSeconds); 
auto result1 = doSomethingThatTakesTwentySeconds(); 
auto result2 = fut.get(); 

通過指定的啓動策略將強制異步執行的,如果你,而它的執行,然後做其他工作,當你需要它的結果將準備。

+2

非常好的答案!我有一個關於最後一個例子的問題:雖然你可以強制'doSomethingThatTakesTenSeconds'在單獨的線程中啓動,但是你不能強制它立即運行,對嗎?如果是這種情況,那麼'fut.get()'仍然可以被阻止。 – GuLearn

+15

是的,但如果需要很長時間才能啓動新線程,那麼您的系統可能會超載或者其調度程序是垃圾。 –

+1

[我在coliru上測量](http://coliru.stacked-crooked.com/a/014dac336cc8374c)有大約100μs到300μs的延遲。大約33毫秒的最大值是好的。我會很想知道在同一個盒子上,VS VS linux g ++的代碼是什麼。 – doug65536

相關問題