C#2.0线程问题(匿名方法)

时间:2008-10-30 13:56:22

标签: c# multithreading .net-2.0 anonymous-methods

我有一个简单的应用程序,代码如下:

   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个线程,每个线程打印一个文件名;但是,每个线程都会打印出'files'数组中最后一个文件的名称。

这是为什么?为什么当前文件'f'变量没有在匿名方法中正确设置?

5 个答案:

答案 0 :(得分:11)

匿名方法将引用保存到封闭块中的变量 - 而不是变量的实际值。

当方法实际执行时(启动线程时)f已被指定为指向集合中的最后一个值,因此所有3个线程都打印该最后一个值。

答案 1 :(得分: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);
   }

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

答案 2 :(得分: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。

答案 3 :(得分:0)

这是因为迭代器(foreach)的底层代码已经在线程开始之前“迭代”了List中的所有值...所以当它们启动时,迭代器“指向”的值是最后一个在列表中...

在迭代中启动线程而不是....

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

由于没有可从所有线程访问的共享内存,我不相信这里可以进行竞争。

答案 4 :(得分:0)

以下内容也可以。

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