问题
The documentation of the lock statement is pretty straightforward:
lock (x) { // Your code... }
where x is an expression of a reference type.
So I should not be allowed to pass a value type as locker for a lock
. I noticed though that I can use a value type that implements an interface. In other words I can do this:
IDisposable locker = default(DisposableStruct);
lock (locker) Console.WriteLine("Thread safe");
struct DisposableStruct : IDisposable
{
public void Dispose() { }
}
This was surprising. I figured out that the reason is that the value type is boxed. According to the documentation:
Boxing is the process of converting a value type to the type
object
, or to any interface type implemented by this value type.
My question is if there are any caveats with using boxed value types as lockers for the lock
statement. Is there any possibility for the reference-type wrapper to change somehow during the execution of the program, causing the thread-safe code to fail?
Update: Here is an example of what worries me. Is it guaranteed that if I run the code bellow a million times, the correct output (1,000,000,000) will always be displayed?
IComparable<int> boxedValueType = 0;
int sharedState = 0;
var tasks = Enumerable.Range(0, 10).Select(_ => Task.Run(() =>
{
for (int i = 0; i < 100_000_000; i++)
lock (boxedValueType)
sharedState++;
})).ToArray();
Task.WaitAll(tasks);
Console.WriteLine(sharedState);
来源:https://stackoverflow.com/questions/56864042/is-it-safe-to-use-a-boxed-value-type-as-a-locker-for-the-lock-statement