Oti*_*iel 51 c# multithreading locking reference
我想lock一个Boolean变量时,我遇到了以下错误:
'bool'不是lock语句所要求的引用类型
似乎在lock语句中只允许引用类型,但我不确定我理解为什么.
安德烈亚斯在评论中说:
当[值类型]对象从一个线程传递到另一个线程时,会生成一个副本,因此线程最终会处理2个不同的对象,这是安全的.
这是真的吗?这是否意味着当我执行以下操作时,我实际上在修改和方法x中的两个不同?xToTruexToFalse
public static class Program {
public static Boolean x = false;
[STAThread]
static void Main(string[] args) {
var t = new Thread(() => xToTrue());
t.Start();
// ...
xToFalse();
}
private static void xToTrue() {
Program.x = true;
}
private static void xToFalse() {
Program.x = false;
}
}
Run Code Online (Sandbox Code Playgroud)
(仅此代码在其状态下显然无用,仅用于示例)
PS:我知道关于如何正确锁定值类型的这个问题.我的问题与如何但与原因无关.
And*_*ber 43
这里只是猜测......
但是如果编译器允许你锁定一个值类型,你最终都不会锁定任何内容......因为每次你将值类型传递给它时lock,你都会传递它的盒装副本; 一个不同的盒装副本.所以锁就好像是完全不同的对象.(因为,他们实际上是)
请记住,当您为类型参数传递值类型时object,它会被装箱(包装)为引用类型.这使它每次发生时都是一个全新的对象.
ole*_*sii 26
您无法锁定值类型,因为它没有sync root记录.
锁定由CLR和OS内部机制执行,这些机制依赖于具有记录的对象,该记录一次只能由单个线程访问 - 同步块根.任何引用类型都有:
Geo*_*ett 18
它扩展到:
System.Threading.Monitor.Enter(x);
try {
...
}
finally {
System.Threading.Monitor.Exit(x);
}
Run Code Online (Sandbox Code Playgroud)
虽然他们会编译,Monitor.Enter/ Exit需要引用类型,因为值类型每次都会被装箱到不同的对象实例,因此每次调用Enter并将Exit在不同的对象上操作.
从MSDN Enter方法页面:
使用Monitor锁定对象(即引用类型),而不是值类型.将值类型变量传递给Enter时,它将被装箱为对象.如果再次将同一个变量传递给Enter,则会将其作为单独的对象加框,并且该线程不会阻塞.在这种情况下,Monitor应该保护的代码不受保护.此外,当您将变量传递给Exit时,仍会创建另一个单独的对象.因为传递给Exit的对象与传递给Enter的对象不同,所以Monitor会抛出SynchronizationLockException.有关更多信息,请参阅概念主题监视器.
我想知道为什么.Net团队决定限制开发人员并允许Monitor只对引用进行操作.首先,您认为锁定System.Int32而不是仅仅为了锁定目的定义专用对象变量是好的,这些储物柜通常不会做任何其他事情.
但是,似乎该语言提供的任何功能必须具有强大的语义,而不仅仅对开发人员有用.所以带有值类型的语义是,只要值类型出现在代码中,它的表达式就会被计算为一个值.因此,从语义的角度来看,如果我们编写`lock(x)'并且x是原始值类型,那么它就像我们所说的那样"锁定一个关键代码块,使变量x的值变为",这听起来更多比奇怪,肯定:).同时,当我们在代码中遇到ref变量时,我们习惯于认为"哦,它是对对象的引用",并暗示引用可以在代码块,方法,类甚至线程和进程之间共享,因此可以用作守护.
换句话说,值类型变量只出现在代码中,以便在每个表达式中评估它们的实际值 - 仅此而已.
我猜这是主要观点之一.
如果从概念上问为什么不允许这样做,我会说答案源于一个事实,即值类型的标识完全等同于其值(这就是使其成为值类型的原因)。
所以在宇宙谈论任何地方的任何int 4在谈论同样的事情 -怎能您可能要求独占访问锁就可以了?