2

lock声明的文档非常简单:

lock (x)
{
    // Your code...
}

其中 x 是引用类型的表达式。

所以我不应该被允许将值类型作为lock. 我注意到虽然我可以使用实现接口的值类型。换句话说,我可以这样做:

IDisposable locker = default(DisposableStruct);
lock (locker) Console.WriteLine("Thread safe");

struct DisposableStruct : IDisposable
{
    public void Dispose() { }
}

这很令人惊讶。我发现原因是值类型被装箱了。根据文档

装箱是将值类型转换为 typeobject或此值类型实现的任何接口类型的过程。

lock我的问题是使用盒装值类型作为语句的储物柜是否有任何警告。引用类型包装器是否有可能在程序执行期间以某种方式更改,从而导致线程安全代码失败?


更新:这是一个让我担心的例子。是否保证如果我将代码运行一百万次,将始终显示正确的输出(1,000,000,000)?

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);
4

0 回答 0