2つのスレッドが、我々はILの下
class Counter
{
private static int s_Number = 0;
public static int GetNextNumber()
{
s_Number++;
return s_Number;
}
}
ここにあなたのクラスのメソッド用に生成さIL code
を見れば、同時にGetNextNumber
メソッドにアクセスしようとすると、同じ番号を取得することが可能である理由は簡単ですコードが生成されています。ご覧のとおり、s_number++
は実際には3つの別々の命令で構成され、2つのスレッドが同時にアクセスして同じ初期値を得ることができます。
Counter.GetNextNumber:
IL_0000: ldsfld UserQuery+Counter.s_Number
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stsfld UserQuery+Counter.s_Number
IL_000C: ldsfld UserQuery+Counter.s_Number
IL_0011: ret
これは、両方のスレッド
同じ値にthread A
をもたらすシナリオが入り、s_Number(IL_0000)の値を取得し、それは値1をロードしますが、この時点で、プロセッサはthread A
を一時停止し、thread B
を開始します。もちろん、s_number
のために定義されたメモリ位置に格納された値はまだ0であり、スレッドBはスレッドAによって使用されたのと同じ値で開始する.1に戻る。スレッドAが再開すると、レジスタは中断時と同様に復元され、それは、0に1を加算し、スレッドBの同じ結果を取り戻す
このクラスは、並行
class CounterLocked
{
private static object o;
private static int s_Number = 0;
public static int GetNextNumber()
{
lock(o)
{
s_Number++;
return s_Number;
}
}
}
CounterLocked.GetNextNumber:
IL_0000: ldc.i4.0
IL_0001: stloc.0 // <>s__LockTaken0
IL_0002: ldsfld UserQuery+CounterLocked.o
IL_0007: dup
IL_0008: stloc.2 // CS$2$0001
IL_0009: ldloca.s 00 // <>s__LockTaken0
IL_000B: call System.Threading.Monitor.Enter
IL_0010: ldsfld UserQuery+CounterLocked.s_Number
IL_0015: ldc.i4.1
IL_0016: add
IL_0017: stsfld UserQuery+CounterLocked.s_Number
IL_001C: ldsfld UserQuery+CounterLocked.s_Number
IL_0021: stloc.1 // CS$1$0000
IL_0022: leave.s IL_002E
IL_0024: ldloc.0 // <>s__LockTaken0
IL_0025: brfalse.s IL_002D
IL_0027: ldloc.2 // CS$2$0001
IL_0028: call System.Threading.Monitor.Exit
IL_002D: endfinally
IL_002E: ldloc.1 // CS$1$0000
IL_002F: ret
InterlockIncrementために生成されたコードは非常に簡単であるにブロックするロックキーワードを使用
public static int GetNextNumber()
{
return Interlocked.Increment(ref s_Number);
}
CounterLocked.GetNextNumber:
IL_0000: ldsflda UserQuery+CounterLocked.s_Number
IL_0005: call System.Threading.Interlocked.Increment
IL_000A: ret
はい、それはそれはそうだ –
可能ですが、それは可能ですので、あなたが状況を処理する必要があります。私はlockキーワードを使用します。 – DGibbs
を保証していないがそれは、可能です –