1

このプログラムは、.txt ファイルの名前を .txtok に変更する必要があります。私のテストディレクトリでは、~10 個のテキストファイルを作成しました。

実行時に、FileNotFoundException がスローされました。欠落しているファイルは、以前のスレッドで既に名前が変更されたファイルでした。

1 回の Loop-Iteration で複数のスレッドが開始されたようです!?

static void Main(string[] args)
    {
        foreach (String s in Directory.EnumerateFiles(@"C:\Test", "*.txt", SearchOption.TopDirectoryOnly))
        {
            new Thread(() =>
            {
                File.Move(s, s + "ok");
            }).Start();                
        }
        Console.ReadKey();
    }

これに似た問題を抱えている人はいますか?

ありがとう

4

2 に答える 2

12

「変更された閉鎖へのアクセス」バグの痛みを経験しています。これは、StackOverflow で報告される最も一般的な問題の 1 つです。詳細については、「変更された閉鎖へのアクセス」を検索するか、この件に関する私の記事を読んでください。

http://ericlippert.com/2009/11/12/clothing-over-the-loop-variable-considered-harmful-part-one/

C# 5 にアップグレードするか、次のようにして修正できます。

    foreach (String s in Directory.EnumerateFiles(@"C:\Test", "*.txt", SearchOption.TopDirectoryOnly))
    {
        string s1 = s;
        new Thread(() =>
        {
            File.Move(s1, s1 + "ok");
        }).Start();                
    }

とはいえ、このコードは適切なコードではありません。そのようなスレッドをたくさん作成しないでください。スレッドは重量級です。新しい従業員を雇うのと同じように、スレッドの作成を扱います。ファイルの名前を変更して解雇するために従業員を雇うことはありません。これは高すぎる。すべてのファイルの名前を変更するために 1 人の従業員を雇います。

于 2013-02-08T23:12:15.453 に答える
1

この問題は、foreachループとラムダ キャプチャの間の相互作用によって発生します。

変数sは、foreachループの反復ごとに上書きされます。これはs、ラムダによってキャプチャされたへの参照new Threadが、新しいスレッドが実行されるまでに変更されたことを意味します。

最初のスレッドは正常に実行されますが、残りのスレッドも同じ値を指しているため、s失敗します。

解決策は、一時変数を作成することです。

foreach (String s in Directory.EnumerateFiles(@"C:\Test", "*.txt", SearchOption.TopDirectoryOnly))
{
  var temp = s;
  new Thread(() => File.Move(temp, temp + "ok")).Start();                
}

@Eric Lippert の提案に従って、PLINQ または TPL を使用してスレッドを管理することを検討してください。

// assume `using System.Linq`

Directory.EnumerateFiles(@"C:\Test", "*.txt", SearchOption.TopDirectoryOnly)
         .AsParallel()
         .Select(f => File.move(f, f + "ok"))
         .ToList();

これにより問題が回避foreachされ、実行時にスレッドの動作を制御できるようになります。

于 2013-02-08T23:14:21.320 に答える