文章出處

 

   這幾天太忙沒時間寫博客,今天回家就簡單的看了下ThreadPool的源碼,發現有一個好玩的東西,叫做”執行上下文“,拽名叫做:”ExecutionContext“。

 

一:ThreadPool的大概流程。

 

第一步:它會調用底層一個helper方法。

 

第二步:走進這個helper方法,我們會發現有一個隊列,并且這個隊列的item必須是QueueUserWorkItemCallback的實例,然后這就激發了我的

           興趣,看看QueueUserWorkItemCallback到底都有些什么?

 

第三步:走到QueueUserWorkItemCallback實例的時候,會依次把callback,state參數給當前類的字段,并且有一個好玩的地方的就是根據

     ExecutionContext.IsFlowSuppressed()來判斷要不要把”當前線程的上下文“給”調用線程“?這個放在后面講,然后我們看到了一

    個 IThreadPoolWorkItem.ExecuteWorkItem()方法,里面有ContextCallback委托的調用,也許這個就是隊列中每一項中要調用

    的方法。

 

第四步:然后我們再回到第二步中的 ThreadPoolGlobals.workQueue.Enqueue(callback, true)方法進去看看,并且我們的callback,state都被封裝成了

      QueueUserWorkItemCallback放到隊列中了,從這個Enqueue方法中,我們看到了一個this.EnsureThreadRequested(),走到方法里面去了

    之后,這時候急迫想去看ThreadPool.RequestWorkerThread()方法,但它是個extern方法,不過從名字上看就是請求工作線程去執行,所以并

    沒有真實的發現到所謂的線程池這個東西。(由于不能窺全貌,可能有些說的不太對)

 

好了,上面的剖析大概就這樣了,其實所有的方法都封裝成了底層的一個類放在一個隊列中,應該是用上面的for來挑選空閑的工作線程去執行我們

的任務,里面還有很多代碼,比較復雜,一時也看不懂什么。

 

二:執行上下文

  剛才第三步說到了”執行上下文“,看到這個方法里面有一個if條件,然后看到有一個 ExecutionContext.IsFlowSuppressed()方法,從名字上

就可以看出叫”阻止流動“,如果為否的話,就用Capture來抓當前線程的”上下文信息“,然后我們就順藤摸瓜的往下看,從這個方法來看,我們依次

去抓取調用線程的”安全設置“,”宿主設置“,”同步信息“,“邏輯調用”,并且可以看到logicalCallContext有值的話,會做一個copy的操作。

 

其實這個logicalCallContext非常有意思,里面是一個KV結構,源碼里面也說了,只要我不IsFlowSuppressed,那么主線程的上下文會flow到

工作線程,那么logicalCallContext怎么設置呢?其實在C#里面的CallContext里面的LogicalSetData和LogicalGetData就可以做這些事情。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             CallContext.LogicalSetData("name", "ctrip");
 6 
 7             Thread.CurrentThread.IsBackground = true;
 8 
 9             ThreadPool.QueueUserWorkItem((o) =>
10             {
11                 var t = Thread.CurrentThread.ManagedThreadId;
12 
13                 var result = CallContext.LogicalGetData("name");
14 
15                 Console.WriteLine("我是工作線程: Name:" + result);
16 
17             });
18 
19             Console.Read();
20         }
21     }

 

可以看到我在主線程設置的值被工作線程讀到了,是不是很有意思,給我們線程間傳值提供了另一種方法,剛才我們也看到,一旦IsFlowSuppressed

了,那么context就返回null,也就阻止了將logicCallContext的信息傳遞給工作線程,可以用ExecutionContext.SuppressFlow()做到,下面具體

看一看。

 1   class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             CallContext.LogicalSetData("name", "ctrip");
 6 
 7             //阻止logical數據流動
 8             ExecutionContext.SuppressFlow();
 9 
10             Thread.CurrentThread.IsBackground = true;
11 
12             ThreadPool.QueueUserWorkItem((o) =>
13             {
14                 var t = Thread.CurrentThread.ManagedThreadId;
15 
16                 var result = CallContext.LogicalGetData("name");
17 
18                 Console.WriteLine("我是工作線程: Name:" + result);
19 
20             });
21 
22             Console.Read();
23         }
24     }

 

現在結論也出來了,去Capture主線程的上下文是需要很多的代碼量,所以如果工作線程用不到主線程的這些信息,那么你應該做到顯示關閉,這樣

對工作線程的性能來說有很大的好處。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()