天天看點

Disruptor-NET和記憶體栅欄

disruptor-net算法(是一種無鎖算法)需要我們自己實作某一種特定的記憶體操作的語義以保證算法的正确性。這時我們就需要顯式的使用一些指令來控制記憶體操作指令的順序以及其可見性定義。這種指令稱為記憶體栅欄。

記憶體一緻性模型需要在各種的程式與系統的各個層次上定義記憶體通路的行為。在機器碼與的層次上,其定義将影響硬體的設計者以及機器碼開發人員;而在進階語言層次上,其定義将影響進階語言開發人員以及編譯器開發人員和硬體設計人員。即,記憶體操作的亂序在各個層次都是存在的。這裡,所謂的程式的執行順序有三種:

(1)程式順序:指在特定cpu上運作的,執行記憶體操作的代碼的順序。這指的是編譯好的程式二進制鏡像中的指令的順序。編譯器并不一定嚴格按照程式的順序進行二進制代碼的編排。編譯器可以按照既定的規則,在執行代碼優化的時候打亂指令的執行順序,也就是上面說的程式順序。并且,編譯器可以根據程式的特定行為進行性能優化,這種優化可能改變算法的形式與算法的執行複雜度。(例如将switch轉化為表驅動序列)

(2)執行順序:指在cpu上執行的獨立的記憶體相關的代碼執行的順序。執行順序和程式順序可能不同,這種不同是編譯器和cpu優化造成的結果。cpu在執行期(runtime)根據自己的記憶體模型(跟編譯器無關)打亂已經編譯好了的指令的順序,以達到程式的優化和最大限度的資源利用。

(3)感覺順序:指特定的cpu感覺到他自身的或者其他cpu對記憶體進行操作的順序。感覺順序和執行順序可能還不一樣。這是由于緩存優化或者記憶體優化系統造成的。

而最終的共享記憶體模型的表現形式是由這三種“順序”共同決定的。即從源代碼到最終執行進行了至少三個層次上的代碼順序調整,分别是由編譯器和cpu完成的。我們上面提到,這種代碼執行順序的改變雖然在單線程程式中不會引發副作用,但是在多線程程式中,這種作用是不能夠被忽略的,甚至可能造成完全錯誤的結果。是以,在多線程程式中,我們有時需要人為的限制記憶體執行的順序。而這種限制是通過不同層次的記憶體栅欄完成的。

thread.memorybarrier: 按如下方式同步記憶體通路:執行目前線程的處理器在對指令重新排序時,不能采用先執行 memorybarrier 調用之後的記憶體通路,再執行 memorybarrier 調用之前的記憶體通路的方式。

按照我個人的了解:就是寫完資料之後,調用memorybarrier,資料就會立即重新整理,另外在讀取資料之前調用memorybarrier可以確定讀取的資料是最新的,并且處理器對memorybarrier的優化小心處理。

disruptor-net正是通過thread.memorybarrier 實作無鎖和線程安全的記憶體操作,看下面是他的atomic的volatile類對常用資料類型的封裝,volatile可以阻止代碼重排序,并且值被更新的時候,會導緻緩存失效,強制回寫到主存中。

Disruptor-NET和記憶體栅欄

paddedinteger類,它使用了7個integer,加上一個對象頭,剛好64個位元組。

本文來自雲栖社群合作夥伴“donet跨平台”,了解相關資訊可以關注“opendotnet”微信公衆号

繼續閱讀