排除锁定字符串有奇怪的行为

时间:2015-06-17 05:09:58

标签: c# string locking

我遇到锁定声明上的问题困扰我:

如果连接两个具有相同表达式(noexcept)的字符串,如下所示,lock语句将此表达式实现为字符串并按预期锁定工作:

"1" + "2"

但是,如果使用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")

请解释一下这种行为:

(我知道在锁定语句中使用字符串(MSDN)违反了锁定准则。)

1 个答案:

答案 0 :(得分:0)

如果代码更改为:

lock ("1" + "2")
{
    Console.WriteLine("outer lock");
    Task.Factory.StartNew(() =>
    {
        lock ("12")
        {//launched afetr 10 second
            Console.WriteLine("inner lock");
        }
    });
    Thread.Sleep(10000);
}

然后"内锁"将在" outter lock"。

之后10秒打印

这意味着" 1" +" 2"字面上等于" 12"。

但是,如果使用.NET Reflector打开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)

商店&#34; 1&#34;在一个局部变量中,并存储&#34; 2&#34;在另一个局部变量中。然后调用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)将打印&#34; 12&#34;在控制台中。

因为c +&#34; 2&#34;在字面上不等于&#34; 1&#34; +&#34; 2&#34;,但&#34; 12&#34;字面上等于&#34; 12&#34;。

这意味着即使是c +&#34; 2&#34;将返回你&#34; 12&#34;,但在内部他们是不同的表达。这意味着你原来的第二个锁定(a +&#34; 2&#34;)&#34;试图锁定不同的表达式,这就是为什么你的第二个代码块会立即执行。