執行緒相關的
背景:為了解決多執行緒競用共享資源的問題,引入資料槽的概念,即將資料存放到執行緒的環境塊中,使該資料只能單一執行緒訪問.(屬於執行緒空間上的開銷)
下面的三種方式是解決多執行緒競用共享資源的通用方式:
①:AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位 解決執行緒競用資源共享問題。
(PS:在主執行緒上設定槽位,使該資料只能被主執行緒讀取,其它執行緒無法訪問)
private void button10_Click(object sender, EventArgs e) { #region 01-AllocateNamedDataSlot命名槽位 { var d = Thread.AllocateNamedDataSlot("userName"); //在主執行緒上設定槽位,使該資料只能被主執行緒讀取,其它執行緒無法訪問 Thread.SetData(d, "ypf"); //宣告一個子執行緒 var t1 = new Thread(() => { Console.WriteLine("子執行緒中讀取資料:{0}", Thread.GetData(d)); }); t1.Start(); //主執行緒中讀取資料 Console.WriteLine("主執行緒中讀取資料:{0}", Thread.GetData(d)); } #endregion #region 02-AllocateDataSlot未命名槽位 { var d = Thread.AllocateDataSlot(); //在主執行緒上設定槽位,使該資料只能被主執行緒讀取,其它執行緒無法訪問 Thread.SetData(d, "ypf"); //宣告一個子執行緒 var t1 = new Thread(() => { Console.WriteLine("子執行緒中讀取資料:{0}", Thread.GetData(d)); }); t1.Start(); //主執行緒中讀取資料 Console.WriteLine("主執行緒中讀取資料:{0}", Thread.GetData(d)); } #endregion }
②:利用特性[ThreadStatic] 解決執行緒競用資源共享問題
(PS:在主執行緒中給ThreadStatic特性標註的變數賦值,則只有主執行緒能訪問該變數,比資料槽效能好,使用方便)
③:利用ThreadLocal執行緒的本地儲存, 解決執行緒競用資源共享問題(執行緒可見性)
(PS:在主執行緒中宣告ThreadLocal變數,並對其賦值,則只有主執行緒能訪問該變數)
④ CallContext 執行緒內可見。當 CallContext 沿執行程式碼路徑往返傳播並且由該路徑中的各個物件檢查時,可將物件新增到其中。
使用場景:當物件需要執行緒內全域性使用。Core不支援
CallContext.SetData(key, temp); DbContext temp = CallContext.GetData(key) as DbContext;
四. 記憶體柵欄-執行緒共享資源
背景:當多個執行緒共享一個變數的時候,在Release模式的優化下,子執行緒會將共享變數載入的cup Cache中,導致主執行緒不能使用該變數而無法執行。
記憶體柵欄:變數在進入記憶體柵欄並讀取時,變數的值已經寫入完畢,離開柵欄時,變數的值被讀取時也已經寫入完畢
解決方案:
①:不要讓多執行緒去操作同一個共享變數,從根本上解決這個問題。
②:利用MemoryBarrier方法進行處理,在此方法之前的記憶體寫入都要及時從cpu cache中更新到 memory;在此方法之後的記憶體讀取都要從memory中讀取,而不是cpu cache。
③:利用VolatileRead/Write方法進行處理。
a = 0 , b = 0; void fun1() { a = 1; b = 1; } void fun2() { while (b == 0) continue; assert(a == 1); }
如果 一個執行緒執行 fun1(),另一個執行緒執行 fun2時,當b=1,a一定等於 1嗎,答案是 不一定,a還有可能是0 具體請看https://zhuanlan.zhihu.com/p/125737864
1 private void button11_Click(object sender, EventArgs e) 2 { 3 #region 01-預設情況(Release模式主執行緒不能正常執行) 4 //{ 5 // var isStop = false; 6 // var t = new Thread(() => 7 // { 8 // var isSuccess = false; 9 // while (!isStop) 10 // { 11 // isSuccess = !isSuccess; 12 // } 13 // Console.WriteLine("子執行緒執行成功"); 14 // }); 15 // t.Start(); 16 17 // Thread.Sleep(1000); 18 // isStop = true; 19 20 // t.Join(); 21 // Console.WriteLine("主執行緒執行結束"); 22 //} 23 #endregion 24 25 #region 02-MemoryBarrier解決共享變數(Release模式下可以正常執行) 26 //{ 27 // var isStop = false; 28 // var t = new Thread(() => 29 // { 30 // var isSuccess = false; 31 // while (!isStop) 32 // { 33 // Thread.MemoryBarrier(); 34 35 // isSuccess = !isSuccess; 36 // } 37 // Console.WriteLine("子執行緒執行成功"); 38 // }); 39 // t.Start(); 40 41 // Thread.Sleep(1000); 42 // isStop = true; 43 44 // t.Join(); 45 // Console.WriteLine("主執行緒執行結束"); 46 //} 47 #endregion 48 49 #region 03-VolatileRead解決共享變數(Release模式下可以正常執行) 50 { 51 var isStop = 0; 52 var t = new Thread(() => 53 { 54 var isSuccess = false; 55 while (isStop == 0) 56 { 57 Thread.VolatileRead(ref isStop); 58 59 isSuccess = !isSuccess; 60 } 61 Console.WriteLine("子執行緒執行成功"); 62 }); 63 t.Start(); 64 65 Thread.Sleep(1000); 66 isStop = 1; 67 68 t.Join(); 69 Console.WriteLine("主執行緒執行結束"); 70 } 71 #endregion 72 73 74 }