條款12:對STL容器線程安全性的期待現(xiàn)實一些 ----------------------------------- 現(xiàn)在考慮下列代碼,。它搜尋一個vector<int>中第一次出現(xiàn)5這個值的地方,,而且,如果它找到了,,就把這個值改為0,。 vector<int> v; 在多線程環(huán)境里,另一個線程可能在行1完成之后立刻修改v中的數(shù)據(jù),。如果是那樣,,行2對first5和v.end的檢測將是無意義的,因為v的值可 能和它們在行1結(jié)束時的值不同,。實際上,,這樣的檢測會產(chǎn)生未定義的結(jié)果,因為另一線程可能插在行1和行2之間,,使first5失效,,或許通過進行一次插入 操作造成vector重新分配它的內(nèi)在內(nèi)存。(那將使vector全部的迭代器失效,。關(guān)于重新分配行為的細(xì)節(jié),,參見條款14。)類似的,,行3中對*first5的賦值是不安全的,,因為另一個線程可能在行2和行3之間執(zhí)行,并以某種方式使first5失效,,可能通過刪除它指向(或至少曾經(jīng)指向)的元素,。 在上面列舉的鎖定方法都不能防止這些問題。行1中begin和end調(diào)用都返回得很快,,以至于不能提供任何幫助,,它們產(chǎn)生的迭代器只持續(xù)到這行的結(jié)束,而且find也在那行返回,。 要讓上面的代碼成為線程安全的,,v必須從行1到行3保持鎖定,很難想象STL實現(xiàn)怎么能自動推斷出這個,。記住同步原語(例如,,信號燈,互斥量,等 等)通常開銷很大,,更難想象實現(xiàn)怎么在程序沒有明顯性能損失的情況下做到前面所說的——以這樣的一種方式設(shè)計——讓最多一個線程在1-3行的過程中能訪問 v。 這樣的考慮解釋了為什么你不能期望任何STL實現(xiàn)讓你的線程悲痛消失,。取而代之的是,,你必須手工對付這些情況中的同步控制。 在這個例子里,,你可以像這樣做: vector<int> v; 一個更面向?qū)ο蟮慕鉀Q方案是創(chuàng)建一個Lock類,,在它的構(gòu)造函數(shù)里獲得互斥量并在它的析構(gòu)函數(shù)里釋放它,這樣使getMutexFor和releaseMutexFor的調(diào)用不匹配的機會減到最小,。這樣的一個類(其實是一個類模板)基本是這樣的: template<typename Container> // 獲取和釋放容器的互斥量 使用一個類(像Lock)來管理資源的生存期(例如互斥量)的辦法通常稱為資源獲得即初始化,你應(yīng)該能在任何全面的C++教材里讀到它,。一個好的開端是Stroustrup的《The C++ Programming Language》[7],, 因為Stroustrup普及了這個慣用法,但你也可以轉(zhuǎn)到《More Effective C++》的條款9,。不管你參考了什么來源,,記住上述Lock是被剝離到最原始的本質(zhì)的。一個工業(yè)強度的版本需要很多改進,,但是那樣的擴充與STL無關(guān),。而 且這個最小化的Lock已經(jīng)足夠看出我們可以怎么把它用于我們一直考慮的例子: vector<int> v; 因為Lock對象在Lock的析構(gòu)函數(shù)里釋放容器的的互斥量,,所以在互斥量需要釋放是就銷毀Lock是很重要的。為了讓這件事發(fā)生,,我們建立一個里 面定義了Lock的新塊,,而且當(dāng)我們不再需要互斥量時就關(guān)閉那個塊。這聽上去像我們只是用關(guān)閉新塊的需要換取了調(diào)用releaseMutexFor的需 要,,但是這是錯誤的評價,。如果我們忘記為Lock建立一個新塊,互斥量一樣會釋放,,但是它可能發(fā)生得比它應(yīng)該的更晚——當(dāng)控制到達封閉塊的末端,。如果我們 忘記調(diào)用releaseMutexFor,我們將不會釋放互斥量,。 而且,,這種基于Lock的方法在有異常的情況下是穩(wěn)健的。C++保證如果拋出了異常,,局部對象就會被銷毀,,所以即使當(dāng)我們正在使用Lock對象時有 異常拋出,Lock也將釋放它的互斥量,。如果我們依賴手工調(diào)用getMutexFor和releaseMutexFor,,那么在調(diào)用 getMutexFor之后releaseMutexFor之前如果有異常拋出,,我們將不會釋放互斥量。 異常和資源管理是重要的,,但是它們不是本條款的主題,。本條款是關(guān)于STL里的線程安全。當(dāng)涉及到線程安全和STL容器時,,你可以確定庫實現(xiàn)允許在一個容器上的多讀取者和不同容器上的多寫入者,。你不能希望庫消除對手工并行控制的需要,而且你完全不能依賴于任何線程支持,。 |
|
來自: 看風(fēng)景D人 > 《STL》