Here is a quick hint on how to make your software thread safe. If you want to increment a member of your class you would probably do something like this:
public void NotSafe()
{
val++;
}
Where val is a member of your class.But this is not thread safe. Doing this involve 4 operations:
- Loading the field and put it on the stack
- Putting 1 on the stack to increment by 1
- Calling add on the stack
- Storing the result in the field
Here is the corresponding IL:
.method public hidebysig instance void NotSafe() cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldarg.0
L_0002: dup
L_0003: ldfld int32 ClassLibrary1.Class1::val
L_0008: ldc.i4.1
L_0009: add
L_000a: stfld int32 ClassLibrary1.Class1::val
L_000f: ret
}
The problem is that anywhere between any of the 4 steps another thread can try to do the same thing. For example if a second thread pup in just after the first one is between step 1 and 2 they will both try to increment the same value and store it on the stack. To resolve this problem you can use a lock like this:
public void SafeLock()
{
lock (valLock)
{
val++;
}
}
But this will generate the following IL:
.method public hidebysig instance void SafeLock() cil managed
{
.maxstack 3
.locals init (
[0] object CS$2$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: ldfld object ClassLibrary1.Class1::valLock
L_0007: dup
L_0008: stloc.0
L_0009: call void [mscorlib]System.Threading.Monitor::Enter(object)
L_000e: nop
L_000f: nop
L_0010: ldarg.0
L_0011: dup
L_0012: ldfld int32 ClassLibrary1.Class1::val
L_0017: ldc.i4.1
L_0018: add
L_0019: stfld int32 ClassLibrary1.Class1::val
L_001e: nop
L_001f: leave.s L_0029
L_0021: ldloc.0
L_0022: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_0027: nop
L_0028: endfinally
L_0029: nop
L_002a: ret
.try L_000f to L_0021 finally handler L_0021 to L_0029
}
As you can see there is a lot more code involve to ensure thread safety, A quicker, faster an easier way to do this s to use Interlocked class. This class will use low level OS call to modify the member. Here’s how to use it:
public void Safe()
{
Interlocked.Increment(ref val);
}
This will be render as two major IL steps:
- Load value from the field and put it on the stack
- Call Increment
Here is the IL representation of this:
.method public hidebysig instance void Safe() cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldarg.0
L_0002: ldflda int32 ClassLibrary1.Class1::val
L_0007: call int32 [mscorlib]System.Threading.Interlocked::Increment(int32&)
L_000c: pop
L_000d: ret
}
Now every time you will see something++ you will know that this is not thread safe and how to fix it.

No comments:
Post a Comment