2015-06-17 4 views
0

я столкнулся с проблемой на заявление блокировки, перепутал меня:замок Исключение над строкой имеет странное поведение

Если конкатенация две строки с таким же выражением ("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")

хотя два выражения имеет тот же результат, но блокировки дескриптор с этим, как два различных экспресс иона так второе заявление замок начал сразу:

lock ("1" + "2") 
{ 
    Task.Factory.StartNew(() => 
    { 
     var a = "1"; 
     lock (a + "2") 
     {//launched immediately 

     } 
    }); 
    Thread.Sleep(10000); 
} 

Может пожалуйста, объяснить такое поведение: (. Я знаю, используя строку в заявлении блокировки (MSDN) нарушает замок рекомендуемую)

+4

Странное поведение происходит из-за String Interning. Более подробную информацию о блокировке на интернированных строках см. В [this SO question] (http://stackoverflow.com/questions/6983714/locking-on-an-interned-string). –

+1

Строки с одинаковым значением не обязательно совпадают памяти. Компилятор автоматически скомпилирует '' 1 "+" 2 "' как '" 12 "', но 'a +" 2 "' создается во время выполнения, приводя к другой строке, даже если она имеет такое же значение. Следовательно, вы блокируете другую переменную. – Enigmativity

+0

@ScottChamberlain - Я не верю, что это имеет какое-либо отношение к интернированию. У вас есть ссылка на источник ссылки? – Enigmativity

ответ

0

Если код изменено на:

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

затем «внутренний замок» будет напечатан через 10 секунд после «outter l Ock».

Это означает, что «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); 
} 

Ил покажет следующий код для внешнего замка

.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» на местную карту.

44445164 +10617451515053691368888

код 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&) 

Код ИЛ:

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

Вы найдете первый string.IsInterned (г) не возвращает ничего, но второй string.IsInterned (е) напечатает «12» в консоли.

Потому что c + "2" буквально не равно «1» + «2», но «12» буквально равно «12».

Это означает, что даже c + "2" вернет вам «12», но внутренне они представляют собой другое выражение. Это означает, что ваш оригинальный второй «lock (a +« 2 ») пытается заблокировать другое выражение, поэтому ваш второй блок кода будет выполнен немедленно.

Смежные вопросы