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 つのスレッドを作成し、各スレッドがいずれかのファイル名を出力する必要があります。ただし、代わりに、各スレッドは「files」配列の最後のファイルの名前を出力します。

どうしてこれなの?匿名メソッドで現在のファイル '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)
   {
       FileInfo f2 = f; //ループ内で宣言された変数
       スレッド t = 新しいスレッド (デリゲート ()
       {
            Console.WriteLine(f2.FullName);
       });
       スレッド。追加(t);
   }

それはあなたが望むように機能するでしょう。

于 2008-10-30T14:22:50.050 に答える
1

これf.FullNameは、変数への参照であり、値ではないためです (これは、使用しようとした方法です)。実際にスレッドを開始するまでに、f.FullName は配列の最後までインクリメントされています。

とにかく、なぜここで 2 回繰り返す必要があるのでしょうか。

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 に答える