假設一個類有一個由多個線程訪問的public int counter
字段。 此int
僅遞增或遞減。 web
要增長此字段,應使用哪一種方法,爲何? 緩存
lock(this.locker) this.counter++;
, Interlocked.Increment(ref this.counter);
, counter
的訪問修飾符更改成public volatile
。 既然我發現了volatile
,我一直在刪除許多lock
語句和Interlocked
的使用。 可是有理由不這樣作嗎? 多線程
我是第二個Jon Skeet的回答,並但願爲想要了解更多關於「volatile」和Interlocked的人們添加如下連接: 優化
原子性,波動性和不變性是不一樣的,第一部分 - (Eric Lippert的編碼中的神話般的冒險) this
Sayonara Volatile - (2012年出現的Joe Duffy博客的Wayback Machine快照) 線程
互鎖功能不會鎖定。 它們是原子的,這意味着它們能夠完成而不會在增量期間進行上下文切換。 因此沒有死鎖或等待的可能性。 code
我會說你應該老是喜歡鎖定和增量。 orm
若是您須要在一個線程中寫入以在另外一個線程中讀取,而且您但願優化器不對變量從新排序操做(由於事情發生在優化器不知道的另外一個線程中),則Volatile很是有用。 這是你如何增量的正交選擇。
若是您想要閱讀更多關於無鎖代碼的信息,以及正確的編寫方法,這是一篇很是好的文章
http://www.ddj.com/hpc-high-performance-computing/210604448
編輯:正如評論中所指出的,這些天我很樂意使用Interlocked
來處理單個變量的狀況 ,這顯然是能夠的。 當它變得更復雜時,我仍然會恢復鎖定......
當您須要遞增時,使用volatile
將無濟於事 - 由於讀取和寫入是單獨的指令。 另外一個線程可能會在您閱讀以後但在您回寫以前更改該值。
就我我的而言,我幾乎老是隻是鎖定 - 以一種明顯正確的方式,比波動性或Interlocked.Increment更容易正確。 就我而言,無鎖多線程是真正的線程專家,其中我不是一個。 若是Joe Duffy和他的團隊構建了一個很好的庫,這些庫能夠在沒有像我構建的東西那麼多鎖定的狀況下進行並行化,這很棒,並且我會在心跳中使用它 - 可是當我本身進行線程處理時,我會嘗試把事情簡單化。
lock(...)有效,但可能阻塞一個線程,若是其餘代碼以不兼容的方式使用相同的鎖,則可能致使死鎖。
Interlocked。*是正確的方法...由於現代CPU支持它做爲原語,因此開銷要少得多。
揮發性自己是不正確的。 嘗試檢索而後寫回修改值的線程仍然可能與執行相同操做的另外一個線程衝突。
「 volatile
」不會取代Interlocked.Increment
! 它只是確保變量不緩存,而是直接使用。
增長變量實際上須要三個操做:
Interlocked.Increment
全部三個部分做爲單個原子操做執行。