1

次のコードを実行すると、IndexOutOfRangeException がスローされます。例外がスローされたとき、i は 2 のように見えます。私の理解では、 i の値が変更された後に新しいスレッドが開始されます。この種の問題からこのコードを安全にする方法はありますか?

int x[2] = {1, 3};
int numberOfThreads = 2;

for (int i = 0; i < numberOfThreads; i++)
{
    new Thread(() =>
    {
        DoWork(x[i]);
    }).Start();
}
4

2 に答える 2

6

問題は、変数 iがキャプチャされており、スレッドが実際に開始されるまでに 2 になっていることです。

代わりにこれを使用してください:

for (int i = 0; i < numberOfThreads; i++)
{
    int value = x[i];
    new Thread(() => DoWork(value)).Start();
}

または:

foreach (int value in x)
{
    int copy = value;
    new Thread(() => DoWork(copy)).Start();
}

または:

for (int i = 0; i < numberOfThreads; i++)
{
    int copyOfI = i;
    new Thread(() => DoWork(x[copyOfI])).Start();
}

いずれの場合も、ラムダ式は、ループの反復ごとに新しい変数をキャプチャします。この変数は、後続の反復では変更されません。

一般に、後で実行されるラムダ式でループ変数をキャプチャすることは避けるべきです。詳細については、このトピックに関するEric Lippert のブログ投稿を参照してください。

C# 5 の時点で、foreachこれが問題になるのを回避するためにループの動作が変更される可能性がありますが、for同等のループは依然として問題になります。

于 2011-10-23T17:41:21.143 に答える
2

代わりにローカル コピーを使用して現在の値を取得するために、ループ変数を閉じています。i

for (int i = 0; i < numberOfThreads; i++)
{
    int localI = i;
    new Thread(() =>
    {
        DoWork(x[localI]);
    }).Start();
}
于 2011-10-23T17:41:42.113 に答える