2008-10-30 60 views
12

我有以下代碼的簡單應用:C#2.0線程問(匿名方法)

FileInfo[] files = (new DirectoryInfo(initialDirectory)).GetFiles(); 
    List<Thread> threads = new List<Thread>(files.Length); 

    foreach (FileInfo f in files) 
    { 
     Thread t = new Thread(delegate() 
     { 
      Console.WriteLine(f.FullName); 
     }); 
     threads.Add(t); 
    } 

    foreach (Thread t in threads) 
     t.Start(); 

允許在 'I = initialDirectory' 目錄我有3個文件說。然後,此應用程序應創建3個線程,每個線程打印一個文件名;但是,每個線程將打印出'文件'數組中最後一個文件的名稱。

這是爲什麼?爲什麼當前文件'f'變量沒有正確設置匿名方法?

回答

11

匿名方法將參考保留在封閉塊中的變量 - 而不是變量的實際值。

當方法被實際執行時(當你啓動線程時)f已被分配指向集合中的最後一個值,因此所有3個線程都會打印最後一個值。

+1

請注意未來的讀者:在C#5.0中這種行爲[實際上會改變](http://stackoverflow.com/a/8899347/137188)。每次迭代都會創建一個新的單獨的循環變量。隨着這一變化,這個問題中的代碼將按照原先的預期行事。 – tcovo 2012-01-24 17:43:45

0

這是因爲f.FullName是一個變量的引用,而不是一個值(這是你如何使用它)。當你真正開始線程時,f.FullName一直增加到數組的末尾。

無論如何,爲什麼在這裏遍歷事物兩次?

foreach (FileInfo f in files) 
{ 
    Thread t = new Thread(delegate() 
    { 
     Console.WriteLine(f.FullName); 
    }); 
    threads.Add(t); 
    t.Start(); 
} 

然而,這仍然是錯誤的,甚至可能更糟,因爲你現在有一個競爭條件,看看哪個線程去更快:寫控制檯項目或迭代到下一個FileInfo的。

+0

在此代碼中,線程在主線程完成修改之前訪問f變量。 – 2008-10-30 15:38:52

6

以下是有關在C#匿名方法和將由編譯器生成的代碼一些很好的文章:

http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx
http://blogs.msdn.com/oldnewthing/archive/2006/08/03/687529.aspx
http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx

我認爲,如果你這樣做:

 
    foreach (FileInfo f in files) 
    { 
     FileInfo f2 = f; //variable declared inside the loop 
     Thread t = new Thread(delegate() 
     { 
      Console.WriteLine(f2.FullName); 
     }); 
     threads.Add(t); 
    } 

它會以你想要的方式工作。

+0

是的,它會。停止修改這個答案。 – Jimmy 2008-10-30 14:44:36

+0

是的,果然有效!謝謝! 我原本以爲foreach循環是自動執行的(每次迭代都會產生新的'f'變量),但我想這對於它的工作沒有任何意義, – John 2008-10-30 15:25:57

0

這是因爲迭代器(foreach)的底層代碼已經在線程啓動前迭代了List中的所有值......所以當它們啓動時,迭代器指向的值是最後一個一個列表...

啓動迭代,而不是內部的線程....

foreach (FileInfo f in files) 
{ 
    string filName = f.FullName; 
    Thread t = new Thread(delegate() 
       { Console.WriteLine(filName); }); 
    t.Start(); 
} 

我不認爲一場比賽是可能的,因爲這裏有來自所有線程訪問沒有共享內存。

+0

有一個競爭條件,f是共享的。您可以通過使每個線程休眠幾毫秒來進行測試:線程t =新線程(委託() { Thread.Sleep(300); Console.WriteLine(f.FullName); }); – 2008-10-30 21:05:27

0

以下內容也適用。

Thread t = new Thread(delegate() 
    { 
     string name = f.Name; 
     Console.WriteLine(name); 
    });