6

フレームワークの並列化に取り組んでいるときに、理由が想像できない奇妙な状態に直面しました! 簡単に説明するために状況を単純化しました。このコードを検討してください:

foreach(var person in personList)
{
    if (person.Name == "Mehran")
        break;
}

複数のスレッド間でpersonList共有されます。

どのような状況で forpersonになることができnull、私はNullReferenceExceptionforを取得できperson.Nameますか?

私が知っているように、人物はここではローカル変数と見なされ、ブロックに入った場合、正常にforeach反復されたので、どのような状況または並列シナリオでも null であってはなりません。personListperson

personListが別のスレッドによって変更された場合や、参照先personが破棄された場合でも、person変数には値が必要です。personが参照されている場所を変更するアクセス権を誰も持っていないためです。

状況を説明するシナリオはありますか?

4

2 に答える 2

11

私が知っているように、ここでは person はローカル変数と見なされ、 foreach ブロックに入った場合、 personList を正常に反復したので、いかなる状況または並列シナリオでも person を null にすることはできません。

反復処理がpersonList成功したからといって、null 値が含まれていないわけではありません。例えば:

List<Person> personList = new List<Person>();
personList.Add(null);

foreach (var person in personList)
{
   // Here, person will be null
}

(さらに、何かがリストを変更している場合、一般的に問題が発生します。ライターに対してスレッドセーフではありませんが、それが問題の一部である必要はないと思います。)

于 2012-12-05T17:46:23.883 に答える
3

変数は変更されません。コンストラクトの実装に使用される反復子foreachは、スレッド セーフではありません。

List<T>.IEnumerable<T>.GetEnumerator()hereのドキュメントから

コレクションが変更されない限り、列挙子は有効なままです。要素の追加、変更、または削除などの変更がコレクションに加えられた場合、列挙子は回復できないほど無効になり、その動作は未定義になります。

列挙子は、コレクションへの排他的アクセスを持っていません。したがって、コレクションの列挙は、本質的にスレッド セーフな手順ではありません。列挙中のスレッド セーフを保証するために、列挙全体でコレクションをロックできます。読み取りおよび書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。

名前空間内のコレクションの既定の実装はSystem.Collections.Generic同期されません。

別のスレッドがリストを変更する可能性がある場合は、それらを繰り返し処理している間は常にリストをロックする必要があります。

于 2012-12-05T17:48:46.190 に答える