這幾天太忙沒時間寫部落格,今天回家就簡單的看了下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主執行緒的上下文是需要很多的程式碼量,所以如果工作執行緒用不到主執行緒的這些資訊,那麼你應該做到顯示關閉,這樣
對工作執行緒的效能來說有很大的好處。