2013-06-26 45 views
2

做容器,如性病::矢量& &朋友(我實際使用的QList)拋出一個異常開捕或者是不確定的行爲,如果一個線程試圖寫入時另一個線程從它讀取容器:做std容器總是在多線程環境中拋出異常嗎?

std::vector<std::string> stuff; 

非關鍵任務(如拼寫檢查),在另一個線程:

try { 
    for (std::string& s : stuff) { 
     //do stuff with s 
    } 
} catch (...) { // Handle all exceptions 
    //bail out of task 
} 

主線程:

stuff.erase(std::remove(someIterator), stuff.end()); 

所以你可以看到在這裏會有一個場景,它可能會有一個無效的迭代器,並且會在讀線程中拋出一個異常 - 這會被捕獲,只是從任務中退出。

但這只是一個場景 - 我可以靠捕例外從這些容器異常,所以我不需要保護與互斥體的載體或字符串?或者會有一些情況下它可以解引用nullptr(或某些東西)並導致SEH異常 - 即我無法捕獲並繼續的東西。我認爲答案是它可能是依賴於實現的,並且很可能會導致未定義的行爲,但我想我會問這個問題。

+2

在C++ 11之前肯定沒有保證,因爲線程沒有正式存在。我認爲他們之後也不會添加任何內容,因爲在單線程程序中,這是不必要的開銷。 –

+0

我認爲答案可能是「沒有保證」,但希望它能在某個地方列入標準。 – gremwell

+1

這是未定義的行爲。你不能依賴拋出異常,代碼更可能出錯。 –

回答

8

一般來說,你不能指望訪問拋出任何異常的無效迭代器。其結果是未定義的行爲:調用可能會拋出,可能會崩潰,它可能會工作多年,然後咬你,它可能會打破你的程序中其他地方無關的東西。

標準禁止這種對對象和標準庫函數的事情:

17.6.4.10/1:

程序的行爲是不確定的,如果調用標準庫函數來自不同的線程可能會引入數據競賽。這可能發生的條件在17.6.5.9中規定。

17.6.5.9/6:

上通過調用一個標準庫的容器或字符串成員函數可以訪問底層的容器獲得的迭代操作,但不得修改它。[注意:特別是,使迭代器無效的容器操作與與該容器關聯的迭代器上的操作發生衝突。 - 尾註]

大多數Qt函數同樣不是線程安全的。

如果您需要在線程之間共享數據,請保護自己免受數據競爭。不要指望一個圖書館爲你做,除非文件說明它。

0

AFAIK該標準沒有提到std :: vector等的多線程行爲。不過,實際上,每個人都將以最直接,最高效的方式來實施事情,這意味着沒有任何線程安全。

MSDN記錄了它們執行STL的行爲。我預計主流實施之間沒有區別。僅供參考MSDN文檔的長和短是如果您有其他線程中的讀者和容器正在修改行爲將是未定義的。

+0

這是不正確的; C++ 11標準非常清楚地表明這種行爲是未定義的。見@ aschelpler的答案。 – Nemo

1

使用無效的迭代器本身是未定義的行爲。它不會像你想象的那樣拋出異常。所以你的想法(除非我誤解了它)即使在無聲的情況下也不會飛。

訪問相同std :: object的數據競爭也是未定義行爲的來源。