在並行方法體中謹慎使用鎖

2021-09-06 05:14:12 字數 1324 閱讀 8596

除了建議88所提到的場合,要謹慎使用並行的情況還包括:某些本身就需要同步執行的場合,或者需要較長時間鎖定共享資源的場合。

在對整型資料進行同步操作時,可以使用靜態類interlocked的add方法,這就極大地避免了由於進行原子操作長時間鎖定某個共享資源所帶來的同步效能損耗。回顧建議83中的例子。

static void main(string args)  

;  int total = 0;  

parallel.for(0, nums.length, () => 

, (i, loopstate, subtotal) => 

,  (x) => interlocked.add(ref total, x)  

);  

console.writeline("total=", total);  

console.readkey();  } 

理論上,針對total的加法操作,需要使用乙個同步鎖,否則就無法避免一次torn read(即兩次mov操作所導致的字段記憶體位址邊界對齊問題)。fcl通過提供interlocked型別解決了這個問題。fcl用來解決簡單型別的原 子性操作還提供了volatile關鍵字。不過這些都不是本建議所要討論的重點。fcl現有的原子性操作為我們同步整型資料的時候,帶來了效能上的提高。 但是,在其他一些場合,我們卻不得不考慮因為同步鎖帶來的損耗。

來看乙個例子:

static void main(string args)  

);  

console.writeline(sample.somecount);  

}  class sampleclass  

public void ******add()  

} 這段**的輸出或許是:

8322580 

顯然,這與我們的期待輸出10000000有很大的差距。為了保證輸出正確,必須為並行中的方法體加鎖(假設sampleclass是外部提供的api,無權進行原始碼修改在其內部加鎖):

object syncobj = new object();  

parallel.for(0, 10000000, (i) => 

}); 

經過以上修改後,**輸出就正確了。但是,這段**也帶來了另外的問題。由於鎖的存在,系統的開銷也增加了,同步帶來的執行緒上下文切換,使我們犧牲 了cpu時間與空間效能。簡單地說,就是這段**還不如不用並行。在建議73中曾經提到過,鎖其實就是讓多執行緒變成單執行緒(因為同時只允許有乙個執行緒訪問 資源)。所以,我們需要謹慎地對待並行方法中的同步問題。如果方法體的全部內容都需要同步執行,就完全不應該使用並行。

Promise在並行中的使用

then的任務被放進微任務裡 settimeout的任務被放進巨集任務裡 微任務做完後再做巨集任務 const task1 newpromise resolve,reject 3000 const task2 newpromise resolve,reject 4000 const task3 ne...

IOS開發中重寫init方法使用需謹慎

經過大神指點,原來這個問題產生的原因是我錯誤的使用了重寫init方法。我重寫的方法如下,原本打算是在初始化的時候就給view乙個標題和背景色 1 自定義初始化 2 instancetype init 7return self 8 但問題恰恰出現在這個重寫的初始化方法中,具體分析如下 先在首頁中建立乙...

在基類構造器中呼叫虛方法需謹慎

最近,在基類的建構函式中呼叫虛方法時,發現了乙個問題。先把問題重現如下 class program public class people protected people public virtual void say 歲了 age public class chinese people publ...