ある日、スレッド化の概念をよりよく理解しようとしていたので、いくつかのテスト プログラムを作成しました。それらの1つは次のとおりです。
using System;
using System.Threading.Tasks;
class Program
{
static volatile int a = 0;
static void Main(string[] args)
{
Task[] tasks = new Task[4];
for (int h = 0; h < 20; h++)
{
a = 0;
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = new Task(() => DoStuff());
tasks[i].Start();
}
Task.WaitAll(tasks);
Console.WriteLine(a);
}
Console.ReadKey();
}
static void DoStuff()
{
for (int i = 0; i < 500000; i++)
{
a++;
}
}
}
2000000 未満の出力が見られることを期待しました。私の想像のモデルは次のとおりです。より多くのスレッドが同時に変数 a を読み取り、a のすべてのローカル コピーが同じになり、スレッドがそれをインクリメントし、書き込みが行われます。このようにして、1 つ以上のインクリメントが「失われます」。
出力はこの推論に反していますが。1 つの出力例 (corei5 マシンから):
2000000
1497903
1026329
2000000
1281604
1395634
1417712
1397300
1396031
1285850
1092027
1068205
1091915
1300493
1357077
1133384
1485279
1290272
1048169
704754
私の推論が正しければ、2000000 がときどき表示され、場合によっては数値が少し小さくなることもあります。しかし、私が見ているのは 2000000 であり、2000000 よりはるかに少ない数です。誰か状況を説明してくれませんか?
編集: このテスト プログラムを書いていたとき、このスレッドを安全にする方法を十分に認識しており、2000000 未満の数値が表示されることを期待していました。出力に驚いた理由を説明しましょう: まず、上記の理由が正しい。2 番目の仮定 (これが私の混乱の元になる可能性が非常に高い): 競合が発生した場合 (実際に発生した場合)、これらの競合はランダムであり、これらのランダムなイベントの発生にはある程度正規分布が予想されます。この場合、出力の最初の行は次のように述べています。2 行目は、ランダム イベントが少なくとも 167365 回発生したことを示しています。0 と 167365 の差は大きすぎます (正規分布ではほとんど不可能です)。したがって、ケースは次のように要約されます: 2 つの仮定のうちの 1 つ (「増分損失」モデルまたは「ある程度正規分布した並列競合」モデル) は正しくありません。どちらが原因ですか?