2014-05-04 24 views
27

當試圖回答另一個Stackoverflow question,我意識到,這簡單的C++ 11的代碼段隱含阻塞調用線程:爲什麼從`std :: async`返回的未來的析構函數被阻塞?

std::async(std::launch::async, run_async_task) 

對我來說這會是十分規範的C++ 11發射任務的方式異步而不關心結果。相反,人們必須明確地創建和分離一個線程(參見提到的問題answer)以實現此目的。

所以這裏是我的問題:是否有任何安全/正確性的原因,std::future的析構函數必須被阻止?如果僅僅阻止get,那麼它是不夠的,否則,如果我對返回值或例外不感興趣,它只是失火而忘了?

+0

剛剛回復了你的評論,在這裏你有一個問題大聲笑 – texasbruce

+1

關於這種行爲有很多討論。查看以下文檔:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3773.pdf和http://www.open-std.org/jtc1/ sc22/wg21/docs/papers/2013/n3776.pdf – nosid

+1

@texasbruce:是的,因爲它真的讓我感到困擾。我確信這是*新的C++ 11y方法,即使我不關心結果(最後只在一個註釋中提到),我也很驚訝地發現它阻塞了。除非有明確的必要性,否則我認爲它不是一個好的界面。 –

回答

23

阻止期貨結構析構函數返回std :: async和線程:這是一個有爭議的話題。下面按時間順序的文件列表反映一些由委員會成員的討論:

雖然有很多的討論,有關於任何改變計劃C++ 14 std :: futurestd :: thread的破壞行爲。

關於你的問題,最有趣的論文可能是Hans Boehm的第二篇論文。我引用一些部分來回答你的問題。

N3679: Async() future destructors must wait

[..]由async()async啓動策略等待他們的析構函數有關的共享狀態恢復期貨準備就緒。這可以防止相關線程繼續運行的情況,並且由於關聯的未來已被銷燬,因此不再需要等待它完成。如果沒有英勇的努力,否則等待完成,這樣的「失控」線程可以繼續運行超過它所依賴的對象的生命週期。

[實施例]

最終的結果很可能是一個跨線程「存儲器粉碎」。如果get()wait()在[期貨]被銷燬之前被調用[..],這個問題當然可以避免。難點[..]是意外的異常可能導致代碼被繞過。因此通常需要某種示範警衛來確保安全。如果程序員忘記添加範圍警衛,攻擊者可能會產生例如一個bad_alloc異常在適當的時候利用這個監督,並導致堆棧被覆蓋。也可能控制用於覆蓋堆棧的數據,從而獲得對進程的控制權。這是一個非常微妙的錯誤,根據我們的經驗,它很可能在實際代碼中被忽略。

更新:光良的行程報告還包含在2013年9月關於會議的結果一些有趣的信息:

The View from the C++ Standard meeting September 2013 Part 2 of 2.

在這個問題上是異步的析構函數不應阻止我們對此進行大量的討論。 [......]得到大量支持的唯一立場是[...]給出了建議,告訴未來的析構函數不會阻塞,除非從異步中返回,這是明顯的例外。經過大量討論後,我們試圖進行的唯一部分是N3776,試圖澄清~future~shared_future除了可能存在異步情況外不會阻止的位置。有人企圖按照C的方式發佈棄用聲明,而不需要替換。這個動議實際上幾乎是提出來的。但是[...]甚至在它到達手術檯之前就已經死亡。

+1

太棒了,正是我期待的答案!仍然不相信這是正確的決定,但至少我現在明白這一點。我想這是RAII的一種極端形式,例如確保文件處理程序在析構函數中關閉。 –

+1

關於它在C++ 14中被修復,這個提議聽起來對我來說是這樣的:[n3773](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3773。 PDF)。或者至少它可以讓你手動「分離」。 –

+2

我確定我已經看到了關於討論結果的一些信息。現在我找到了它並更新了答案。 – nosid

相關問題