12

我有一个带有以下代码的简单应用程序:

   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”变量没有在匿名方法中正确设置?

4

5 回答 5

11

匿名方法在封闭块中保留对变量的引用——而不是变量的实际值。

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

于 2008-10-30T13:57:57.183 回答
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)
   {
       文件信息 f2 = f; //在循环中声明的变量
       线程 t = 新线程(委托()
       {
            Console.WriteLine(f2.FullName);
       });
       线程。添加(t);
   }

它会按照你想要的方式工作。

于 2008-10-30T14:22:50.050 回答
1

这是因为f.FullName是对变量的引用,而不是值(这是您尝试使用它的方式)。当您实际启动线程时,f.FullName 一直递增到数组的末尾。

无论如何,为什么要在这里迭代两次?

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

但是,这仍然是错误的,甚至可能更糟,因为您现在有一个竞争条件来查看哪个线程运行得更快:写入控制台项或迭代到下一个 FileInfo。

于 2008-10-30T14:01:09.423 回答
0

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

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

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

我不相信这里有可能发生比赛,因为所有线程都无法访问共享内存。

于 2008-10-30T17:28:36.430 回答
0

以下内容也可以。

    Thread t = new Thread(delegate()
    {
        string name = f.Name;
        Console.WriteLine(name);
    });
于 2008-10-30T20:51:19.267 回答