這幾天太忙沒時間寫博客,今天回家就簡單的看了下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主線程的上下文是需要很多的代碼量,所以如果工作線程用不到主線程的這些信息,那么你應該做到顯示關閉,這樣
對工作線程的性能來說有很大的好處。
文章列表