1. 程式人生 > 實用技巧 >執行緒相關的

執行緒相關的

背景:為了解決多執行緒競用共享資源的問題,引入資料槽的概念,即將資料存放到執行緒的環境塊中,使該資料只能單一執行緒訪問.(屬於執行緒空間上的開銷)

  下面的三種方式是解決多執行緒競用共享資源的通用方式:

  ①: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         }