簡體   English   中英

ThreadPool.QueueUserWorkItem的意外行為

[英]Unexpected behaviour for ThreadPool.QueueUserWorkItem

請檢查下面的代碼示例:

public class Sample
{
    public int counter { get; set; }
    public string ID;
    public void RunCount()
    {
        for (int i = 0; i < counter; i++)
        {
            Thread.Sleep(1000);

            Console.WriteLine(this.ID + " : " + i.ToString());
        }
    }
}

class Test
{
    static void Main()
    {
        Sample[] arrSample = new Sample[4];

        for (int i = 0; i < arrSample.Length; i++)
        {
            arrSample[i] = new Sample();
            arrSample[i].ID = "Sample-" + i.ToString();
            arrSample[i].counter = 10;
        }

        foreach (Sample s in arrSample)
        {
            ThreadPool.QueueUserWorkItem(callback => s.RunCount());
        }

        Console.ReadKey();
    }

}

此示例的預期輸出應類似於:

Sample-0 : 0 
Sample-1 : 0 
Sample-2 : 0 
Sample-3 : 0 
Sample-0 : 1 
Sample-1 : 1 
Sample-2 : 1 
Sample-3 : 1
.
. 
.

但是,當您運行此代碼時,它將顯示以下內容:

Sample-3 : 0 
Sample-3 : 0 
Sample-3 : 0 
Sample-3 : 1 
Sample-3 : 1 
Sample-3 : 0 
Sample-3 : 2 
Sample-3 : 2
Sample-3 : 1 
Sample-3 : 1
.
. 
.

我可以理解,線程執行的順序可能不同,因此計數不會以循環方式增加。 但是,我無法理解為什么所有ID都顯示為Sample-3 ,而執行顯然是相互獨立的。

不同的對象是否與不同的線程一起使用?

這是舊的修改后的閉包問題。 您可能希望查看: Threadpools -類似問題的可能線程執行順序問題 ,以及Eric Lippert的博客文章Closing over loop variable被認為對於理解該問題有害

從本質上講,你所獲得的lambda表達式是捕獲變量 s而不是聲明lambda的變量 因此,隨后到變量的值作出改變到委托可見。 RunCount方法將運行的Sample實例將取決於委托實際執行時變量s (其值)引用實例

此外,由於委托(編譯器實際上重用了相同的委托實例)正在異步執行,因此無法保證每次執行時這些值是什么。 您目前看到的是foreach循環任何委托調用之前在主線程上完成(預期 - 在線程池上調度任務需要時間)。 因此, 所有工作項最終都會找到循環變量的“最終”值。 但這無法保證; 嘗試在循環內插入一個合理持續時間的Thread.Sleep ,您將看到不同的輸出。


通常的解決方法是:

  1. 引入環體的內部的另一變量。
  2. 將該變量分配給循環變量的當前值。
  3. 捕獲'copy'變量而不是lambda中的循環變量。

     foreach (Sample s in arrSample) { Sample sCopy = s; ThreadPool.QueueUserWorkItem(callback => sCopy.RunCount()); } 

現在每個工作項“擁有”循環變量的特定值。


在這種情況下的另一個選擇是通過不捕獲任何東西完全避開問題:

ThreadPool.QueueUserWorkItem(obj => ((Sample)obj).RunCount(), s);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM