線程基礎之遺漏和擴展部分
這裏我們隻是關注了一些多線程之間共享變量的簡單使用問題。這些是任何一個寫多線程程序的人,都應該熟悉的最基礎的問題。我們忽略了一些其他多線程實現提供的工具。它們雖然很少被用到,但是對於你的程序仍然很有必要。
其他鎖類型
大多數環境提供可重入鎖,即被一個單線程多次持有,比如java synchronized 塊就有這種鎖的特性行為。通常讀寫鎖也提供這個功能,即一個鎖可以同時被多個“讀”線程持有,但隻能同時被一個“寫”線程持有。
條件變量等
對於一個線程等待某個特殊條件符合要求,條件變量時最普遍的機製,比如等待一個共享隊列是不為空的。這些提供wait()調用來釋放並且重新獲取一個鎖,掛起線程一段時間,直到它被另個線程喚醒(”notified” or signalled”)。如果一個線程不能被正確喚醒,條件變量很容易引入死鎖到應用程序。因此它們應該被謹慎的運用。但如果我們的關注點隻是我們的程序而不是產生的錯誤結果,一個wait(cv, lock)調用行為就如同一個unlock(lock)加lock(lock)的序列。從而新的問題與這篇文章非常正交。
非阻塞鎖獲取
大多數語言或者線程庫提供一個trylock()原函數,可以獲得一個鎖,像lock()的功能,或者返回一個錯誤的提示,但它從來不會阻塞,這非常容易被我們的模型容納,我們允許trylock()返回一個錯誤的提示,即使這個鎖是可獲得,或者至少是我們代碼的原因,雖然這有可能發生。 因為一些微妙的原因這個假設不允許通過程序實現 ,否則可以看出順序一致性的錯覺 ,類似觀察申請鎖時調用超時。實際上,不允許在代碼中濫用trylock(),並且應該去用其他一些原語。
從順序一致性“逃離”
很多語言允許通過侵犯順序一致性來獲取同步變量,即使程序不包含數據競爭。很多平台用這些來顯著提升性能,以一個更複雜的程序模型為代價,在這裏我們不討論該問題。例如java.util.concurrent.atomic‘s lazySet() (Java 6 +) and weakCompareAndSet() 允許一些實現以和順序一致性違背的方式來進行重排序。C++0x atomic objects 支持內存排序限定參數的操作,它們大部分和順序一致性違背。
曆史和致謝
順序一致性概念是被Lamport引入的,How to Make a Multiprocessor Computer that Correctly Executes Multiprocess Programs. sequential-consistency-for-data-race-free-programs方式從一開始就已經是Ada編程模型的基礎。它作為一個被機器架構有效支撐的模型大約20年前被Sarita Adve研究的更詳細。(See for example Adve and Hill, Weak Ordering – A New Definition, ISCA 1990.) 如我們以上所指出的,Posix線程模型至少和它相似,但是很多Posix使用者不知道這一點。我們相信windows native 線程模型也是取向和它類似,但是直到最近還沒有關於該主題的很多討論。
最近幾年java和C++0x內存模型是明確的基於這種方式實現的。除了這些方麵的論文作者,還有主要的貢獻者包括Lawrence Crowl, Doug Lea, Paul McKenney, Clark Nelson, Bratin Saha, and Herb Sutter.
Paul McKenney也發表了一篇“frequently asked questions”文章,內容和這篇文章類似。Sarita Adve在實現章節提供一些內容。我們也借用了Herb Sutter例子。
很多讀者在早期的草稿中提供了有用的評論。Rob Schreiber提供了特殊的擴展。
最後更新:2017-05-23 10:02:27