マルチスレッド アプリケーションを扱い慣れている方なら、入力したコードに不備があることにお気付きかもしれません。Calculator の計算を実行する各メソッドのコード行を思い出してください。
varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;
これらの 2 行のコードでは、パブリック変数 varTotalCalculations をインクリメントし、ローカル変数 varTotalAsOfNow をこの値に設定します。次に、この値は frmCalculations に返され、ラベル コントロールに表示されます。ところで、返された値は正しいでしょうか。シングル スレッドの実行によって動作している場合に限っては、確実に正しい値が返されます。しかし、複数のスレッドが実行されている場合は、正しい値が返されることは保証されません。各スレッドは、変数 varTotalCalculations をインクリメントできます。あるスレッドによってこの変数がインクリメントされた後、インクリメントされた値が varTotalAsOfNow にコピーされる前に、別のスレッドによってインクリメントされ、変数の値が変更されてしまう可能性があります。つまり、実際には、各スレッドが正しくない結果をレポートしている可能性があります。Visual C# には、スレッドの同期を可能にし、各スレッドが常に正確な結果を返すようにするための lock ステートメント (C# リファレンス) が用意されています。lock の構文は次のとおりです。
lock(AnObject)
{
// Insert code that affects the object.
// Insert more code that affects the object.
// Insert more code that affects the object.
// Release the lock.
}lock ブロックが入力されると、指定された式の実行は、指定されたスレッドが対象のオブジェクトを排他ロックするまでブロックされます。上記の例では、実行は AnObject に対してブロックされます。lock は、値ではなく参照を返すオブジェクトで使用します。このようにして、実行が他のスレッドによって干渉されることなく 1 つのブロックとして続行されます。1 つの単位として実行されるステートメントのセットは、分割不可能であることになります。} を検出すると、式が解放され、スレッドは通常どおりに実行されます。
アプリケーションにロック ステートメント追加するには
コード エディタで Calculator.cs を開きます。
次のコードが記述されたすべての場所を探します。
varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;
計算メソッドごとに 1 つずつ、このコードの 4 つのインスタンスが必要です。
コードを次のように変更します。
lock(this)
{
varTotalCalculations += 1;
varTotalAsOfNow = varTotalCalculations;
}作業内容を保存し、前の例と同様にテストします。
プログラムのパフォーマンスにわずかな影響があることがわかります。これはコンポーネントに排他ロックがかけられるときにスレッドの実行が停止するために起こります。この方法によって、正確さは保証されますが、複数のスレッドによって得られるパフォーマンス上の利点が若干損なわれます。スレッドのロックは、必要かどうかを慎重に検討して、不可欠な場合にだけ実装してください。