2012-03-05 90 views
7

我一直聽說線程安全。那究竟是什麼以及如何以及從哪裏學習編寫線程安全代碼?線程安全編程

此外,假設我有2個線程,一個寫入一個結構,另一個讀取它。這有危險嗎?有什麼我應該找的?我不認爲這是一個問題。這兩個線程不會(完全不能)在同一時間訪問該結構。

另外,有人可以告訴我如何在這個示例中:https://stackoverflow.com/a/5125493/1248779我們在併發問題上做得更好。我不明白。

+1

[Google]上的前幾個匹配項(https://www.google.com/#hl=zh-CN&sclient=psy-ab&q=c%2B%2B+thread-safe+programming&pbx=1&oq=c%2B%2B +線程安全+編程與水性= F&AQI = AQL = gs_sm = 3&gs_upl = 532l4457l0l4950l27l12l0l2l2l0l481l3318l0.4.3.4.1l12l0&gs_l = hp.3 ... 532l4457l0l4950l27l12l0l2l2l0l481l3318l0j4j3j4j1l12l0&BAV = on.2,or.r_gc.r_pw.r_cp.r_qf,cf.osb&計劃生育= 3e688b5b28c62a4d及白車身= 1306&bih = 867)似乎很有啓發性。 – jogojapan 2012-03-05 07:14:37

+1

對不起,我試過了,我發現它太複雜了我需要一個簡單的例子和​​workarround我猜 – Kam 2012-03-05 07:18:27

+0

爲什麼兩個線程不能同時訪問結構?除非你保護它(例如使用信號量),否則你不知道第一個線程是否寫入了結構的一部分,然後OS調度器決定運行讀取結構的線程,該線程現在不會被完全更新。 – 2012-03-05 07:21:23

回答

4

線程安全是「併發編程」的總標題下更大的一組問題的一個方面。我建議圍繞這個主題閱讀。

你假設兩個線程不能同時訪問結構是不好的。第一:今天我們有多核心機器,所以兩個線程可以在同一時間運行。第二:即使在單個核心機器上,給予任何其他線程的時間片也是不可預測的。你必須預見那個「其他」線程可能正在處理的任意時間。見下面我的「機會之窗」的例子。

線程安全的概念正是爲了回答「這是否有危險」的問題。關鍵問題是,在一個線程中運行的代碼是否有可能獲得某些數據的不一致視圖,這種不一致的發生是因爲它在運行另一個線程時正處於數據更改的中間。

在你的例子中,一個線程正在讀取一個結構,同時另一個線程正在寫入。假設有兩個相關的領域:

{ foreground: red; background: black } 

和作家是改變這些

foreground = black; 
      <=== window of opportunity 
    background = red; 

如果讀者在這一點機會之窗讀取值的過程,然後它看到一個「胡說八道「組合

{ foreground: black; background: black } 

這種模式的本質這是很短的時間,而我們正在發生變化,系統變得不一致,讀者不應該使用的值。一旦我們完成我們的更改,再次閱讀變得安全。

因此,我們使用Stefan提到的CriticalSection API來防止線程看到不一致的狀態。

+1

C#?這被標記爲C++。 – Pubby 2012-03-05 07:21:50

+0

嗨,請檢查該http://stackoverflow.com/a/5125493/1248779他說,這是你可以做的東西線程safe..I沒有看到it..What不同的之前和之後incapsulation? – Kam 2012-03-05 07:49:54

+1

這是正確的,但甚至可能更糟糕。我不知道這些顏色是什麼類型,但是如果它們不是基本的原子類型,那麼您實際上直接在作業中獲得了機會的_windows_。 – 2012-03-05 08:04:59

1

在WIN32平臺上編寫多線程C++程序時,需要保護某些共享對象,以便只有一個線程可以在任何給定時間從不同線程訪問它們。您可以使用5個系統功能來實現此目的。它們是InitializeCriticalSection,EnterCriticalSection,TryEnterCriticalSection,LeaveCriticalSection和DeleteCriticalSection。

而且也許這個鏈接可以幫助: how to make an application thread safe?

http://www.codeproject.com/Articles/1779/Making-your-C-code-thread-safe

0

線程安全是當某個代碼塊被保護不被多個線程訪問時。這意味着被操縱的數據總是保持一致的狀態。

一個常見的例子是生產者消費者問題,其中一個線程的數據結構中讀取,而另一個線程寫入相同的數據結構:Detailed explanation

+0

這不是關於保護一段代碼,而是關於保護一大塊數據。 – 2012-03-05 07:36:52

+1

@Alex,好吧,它實際上都是。那一刻你涉及的設備(甚至只是控制檯窗口或文件),你可能還需要鎖定,即使你是不是在自己的程序修改任何數據。 – 2012-03-05 09:05:29

7

這是一個非常深刻的話題。在心臟線程通常是通過同時使用多個內核來讓事情快速發展;或者如果您沒有一種很好的方法將操作與「主」線程交錯,則可以在後臺執行長操作。後者在UI編程中很常見。

您的場景是經典麻煩點之一,也是第一批遇到的人之一。在成員真正獨立的情況下建立一個結構是很少見的。想要修改結構中的多個值以保持一致性是很常見的。在沒有任何預防措施的情況下,很有可能修改第一個值,然後讓另一個線程讀取結構並在第二個值寫入之前對其進行操作。

簡單的例子是2d圖形的「點」結構。你想把這個點從[2,2]移到[5,6]。如果你有一個不同的線程繪製一條線,那麼你可以很容易地畫到[5,2]。

這是真正的冰山一角。有很多優秀的書籍,但學習這個空間通常會這樣:

  1. 呃哦,我剛剛從不一致的狀態讀取該東西。
  2. 嗯哦,我只是從2個線程修改那件事,現在它是垃圾。
  3. 耶!我瞭解了鎖
  4. 哇,我有很多的鎖,當我有很多鎖在嵌套代碼中時,有時似乎只是有時候掛。
  5. Hrm。我需要停止在飛行中鎖定這個鎖定,我似乎錯過了很多地方;所以我應該將它們封裝在一個數據結構中。
  6. 這個數據結構很棒,但現在我似乎一直在鎖,我的代碼和單線程一樣慢。
  7. 條件變量很奇怪
  8. 這很快,因爲我巧妙地鎖定了事物。人力資源管理。有時數據損壞。
  9. 哇......聯鎖了嗎?你有沒有?
  10. 嘿,看看沒有鎖,我做這個東西叫自旋鎖。
  11. 條件變量。恩......我明白了。
  12. 你知道是什麼,怎麼樣我就開始思考如何在完全獨立的方式對這個東西進行操作,pipelineing我的操作,並具有儘可能少的橫紗依賴儘可能...

顯然,這是不所有關於條件變量。但是有很多問題可以通過線程解決,也可能有很多方法可以解決,甚至有更多的方法可以解決問題。

3

究竟是什麼?

簡而言之,可以在併發上下文中執行的程序,沒有與併發有關的錯誤。

如果的ThreadA和ThreadB讀和/或無差錯寫數據,並使用適當的同步,則程序可以是線程。這是一個設計選擇 - 使對象線程安全可以通過多種方式完成,而更復雜的類型可以使用這些技術的組合進行線程安全。

,以及如何和我在哪裏可以學習程序線程安全的代碼?

boost/libs/thread /可能是一個很好的介紹。這個話題非常複雜。

C++的11個標準庫提供了鎖,原子能和線程的實現 - 它使用這些將是一個良好的閱讀任何寫得很好的程序。標準庫是在boost實現之後建模的。

另外,假設我有2個線程,一個寫入一個結構,另一個讀取它。這有危險嗎?有什麼我應該找的?

是的,它可能是危險的和/或可能會產生不正確的結果。試想一下,線程可能會在任何時候耗盡時間,然後另一個線程可能會讀取或修改該結構 - 如果您沒有保護它,它可能正在更新中。一個常見的解決方案是鎖,它可以用來防止另一個線程在讀/寫期間訪問共享資源。

+0

正如我提到的一前一後請檢查該stackoverflow.com/a/5125493/1248779他說,這是你可以做的東西線程safe..I沒有看到it..What不同的之前和之後incapsulation? – Kam 2012-03-05 07:52:29

+0

@ user1248779西奧的'INT myFunc的(結構myState *狀態)'不是線程安全的,如果'@a state'可能被另一個線程寫入。如果'@a state'是線程本地數據,那麼它將是線程安全的。哎呀,它不是封裝,使得這個程序線程安全 - 這是因爲對象'@a state'指向的是線程本地而不是全局。還要注意Theo確實指定'@a state'是線程本地的。 – justin 2012-03-05 08:13:15

+0

那他爲什麼要封裝x和y並創建一個結構?有什麼好處?請裸露我真的沒有看到它 – Kam 2012-03-05 08:18:18

0

要回答這個問題的第二部分:想象一下,兩個線程訪問std::vector<int> data:並行

//first thread 
if (data.size() > 0) 
{ 
    std::cout << data[0]; //fails if data.size() == 0 
} 

//second thread 
if (rand() % 5 == 0) 
{ 
    data.clear(); 
} 
else 
{ 
    data.push_back(1); 
} 

運行這些線程,你的程序將會崩潰,因爲std::cout << data[0];可能data.clear();後直接執行。

您需要知道,在線程代碼的任何時候,線程可能會中斷,例如,在檢查(data.size() > 0)後,另一個線程可能會變爲活動狀態。雖然第一個線程在單線程應用程序中看起來是正確的,但它不在多線程程序中。

1

線程安全是一個簡單的概念:它是「安全的」,以在一個線程,而另一個線程執行操作A的執行操作B,其可以是或可以不是相同的操作A.這可以被擴展到覆蓋許多線程。在此背景下,「安全」是指:

  • 由螺紋

的實際操作A和B可以觀察到沒有未定義行爲

  • 數據結構的所有不變量都保證是很重要的。如果兩個線程讀取一個普通的int變量,那麼這很好。但是,如果有任何線程可能寫入該變量,並且沒有同步以確保讀取和寫入不能一起發生,那麼您有一個數據競爭,這是未定義的行爲,並且這是而不是線程安全。

    這同樣適用於您詢問的情況:除非您採取了特殊的預防措施,否則在另一個線程寫入同一時間的同時,不得不從一個結構中讀取一個線程。 如果你可以保證線程不能同時訪問數據結構,通過某種形式的同步,如互斥鎖,臨界區,信號量或事件,那麼就沒有問題。

    可以使用的東西像互斥和臨界區,以防止對某些數據的併發訪問,這樣寫線程訪問,當它被寫入數據的唯一線程,讀線程訪問數據的時候纔會線程它正在閱讀,從而提供我剛剛提到的保證。這因此避免了上述未定義的行爲。

    但是,您仍然需要確保您的代碼在更廣的上下文中是安全的:如果您需要修改多個變量,那麼您需要在整個操作中保持互斥鎖而不是每個單獨的訪問,否則你可能會發現你的數據結構的不變式可能不會被其他線程觀察到。

    也有可能數據結構對於某些操作可能是線程安全的,但對其他操作可能不是。例如,如果一個線程正在推送隊列中的項目,而另一個正在將項目從隊列中彈出,但是如果兩個線程正在推送項目,或者兩個線程正在彈出項目,則單個生產者的單個使用者隊列將會正常。

    在你引用的例子中,問題是全局變量在所有線程之間是隱式共享的,因此如果有任何線程可以修改它們,所有訪問都必須通過某種形式的同步(例如互斥鎖)來保護。另一方面,如果每個線程都有單獨的數據副本,那麼該線程可以修改其副本,而不用擔心來自任何其他線程的併發訪問,並且不需要同步。當然,如果兩個或多個線程要在相同的數據上運行,您總是需要同步。

    我的書,C++ Concurrency in Action涵蓋了什麼是線程安全的東西,如何設計線程安全的數據結構,以及用於此目的的C++同步原語,如std::mutex