该lock
声明的文档非常简单:
lock (x) { // Your code... }
其中 x 是引用类型的表达式。
所以我不应该被允许将值类型作为lock
. 我注意到虽然我可以使用实现接口的值类型。换句话说,我可以这样做:
IDisposable locker = default(DisposableStruct);
lock (locker) Console.WriteLine("Thread safe");
struct DisposableStruct : IDisposable
{
public void Dispose() { }
}
这很令人惊讶。我发现原因是值类型被装箱了。根据文档:
装箱是将值类型转换为 type
object
或此值类型实现的任何接口类型的过程。
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);