字符串上的排除锁定具有奇怪的行为
本文关键字:排除 锁定 字符串 | 更新日期: 2023-09-27 17:56:33
我遇到了让我感到困惑的锁定语句问题:
如果将两个字符串与相同的表达式("1" + "2"
)连接起来,如下所示,lock语句将此表达式实现为字符串,并且锁定按预期工作:
lock ("1" + "2")
{
Task.Factory.StartNew(() =>
{
lock ("1" + "2")
{//launched afetr 10 second
}
});
Thread.Sleep(10000);
}
但是,如果首先lock ("1" + "2")
发生变化,var a="1"; lock (a + "2")
虽然两个表达式的结果相同,但 Lock 语句将其作为两个不同的表达式处理,因此第二个 Lock 语句立即启动:
lock ("1" + "2")
{
Task.Factory.StartNew(() =>
{
var a = "1";
lock (a + "2")
{//launched immediately
}
});
Thread.Sleep(10000);
}
请解释一下这种行为:
(我知道在锁定语句 (MSDN) 中使用字符串违反了锁定准则。
如果代码更改为:
lock ("1" + "2")
{
Console.WriteLine("outer lock");
Task.Factory.StartNew(() =>
{
lock ("12")
{//launched afetr 10 second
Console.WriteLine("inner lock");
}
});
Thread.Sleep(10000);
}
然后"内锁"将在"外锁"后 10 秒打印。
这意味着"1"+"2"字面上等于"12"。
但是,如果使用 .NET 反射器打开以下各项的 IL 代码:
lock ("1" + "2")
{
Console.WriteLine("outer lock");
Task.Factory.StartNew(() =>
{
var a = "1";
lock (a + "2")
{//launched afetr 10 second
Console.WriteLine("inner lock");
}
});
Thread.Sleep(10000);
}
IL 将显示外部锁的以下代码
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] bool flag,
[1] string str,
[2] bool flag2)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldstr "12"
L_0008: dup
L_0009: stloc.1
L_000a: ldloca.s flag
L_000c: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
L_0011: nop
L_0012: nop
L_0013: ldstr "outer lock"
L_0018: call void [mscorlib]System.Console::WriteLine(string)
IL 代码:
L_0003: ldstr "12"
L_0008: dup
L_0009: stloc.1
L_000a: ldloca.s flag
最终将"12"保存到本地变量中
"内部锁"的 IL 代码是
.method private hidebysig static void <Main>b__3() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
.maxstack 2
.locals init (
[0] string str,
[1] bool flag,
[2] string str2,
[3] bool flag2)
L_0000: nop
L_0001: ldstr "1"
L_0006: stloc.0
L_0007: ldc.i4.0
L_0008: stloc.1
L_0009: ldloc.0
L_000a: ldstr "2"
L_000f: call string [mscorlib]System.String::Concat(string, string)
L_0014: dup
L_0015: stloc.2
L_0016: ldloca.s flag
L_0018: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
IL 代码:
L_0001: ldstr "1"
L_0006: stloc.0
L_0007: ldc.i4.0
L_0008: stloc.1
L_0009: ldloc.0
L_000a: ldstr "2"
L_000f: call string [mscorlib]System.String::Concat(string, string)
将"1"存储在一个局部变量中,将"2"存储在另一个局部变量中。然后调用 String.Concat。
如果您尝试其他代码:(在另一个控制台程序中)
var c = "1" + "2";
var d = c + "2";
Console.WriteLine(string.IsInterned(d));
var e = "12";
Console.WriteLine(string.IsInterned(e));
您将找到第一个字符串。IsInterned(d) 不返回任何内容,只返回第二个字符串。IsInterned(e) 将在控制台中打印"12"。
因为c+"2"不是字面上等于"1"+"2",而是"12"字面上等于"12"。
这意味着即使 c + "2" 也会返回 "12",但在内部它们是不同的表达式。这意味着您原来的第二个"锁定(a + "2")"正在尝试锁定不同的表达式,这就是为什么您的第二个代码块将立即执行的原因。