在看Artech的部落格時發現他的這篇難道調用ThreadPool.QueueUserWorkItem()的時候,真是必須調用Thread.Sleep(N)嗎? 講到的一個匿名方法造成的問題,在文章後面,有老趙的回複,並且給出瞭解決方案(查看老趙的“警惕匿名方法造成的變數共用”)。其實不止匿名方法有這個困擾,我們在操作集合的時候,都應該全面考慮到”變數共用“問題。下面就貼一下自己加了幾行注釋的Artech的源碼,從我自己的角度來分析一下:
代碼
class Program
{
static void Main(string[] args)
{
List<Action> actions = new List<Action>();
actions.Add(() => Console.WriteLine("A1"));
actions.Add(() => Console.WriteLine("A2"));
actions.Add(() => Console.WriteLine("A3"));
actions.Add(() => Console.WriteLine("A4"));
foreach (var action in actions)
{
//var tmpAction = action; //線程執行這個委託方法就輸出正常
//ThreadPool.QueueUserWorkItem(state => tmpAction(), null);
ThreadPool.QueueUserWorkItem(state => action(), null);
//Thread.Sleep(1); //不管有沒有這一行 都是有問題的
}
Console.Read();
}
}
運行後,我們看到的結果和我們理想的相差甚遠(加上Thread Sleep(1)那一行運行結果有時也不全是我們想要的結果)。
其實我們完全可以這樣理解:在foreach迴圈的時候,action是一個委託方法引用,是參考型別,線程執行的時候,都將執行action變數所在的同一引用地址上的委託方法。而我們將action賦值給一個中間變數tmpAction後,每迴圈一次,就相當於在記憶體上重新分配了一段空間,然後線程執行一個新引用地址上的委託方法,這就避免了老趙所說的“匿名方法造成的變數共用”。
ps:我在早前一篇部落格裡講到匿名方法的“一個需要注意的地方”的時候也提到了這一點,不知各位是否贊同。