Если код изменено на:
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 ») пытается заблокировать другое выражение, поэтому ваш второй блок кода будет выполнен немедленно.
Странное поведение происходит из-за String Interning. Более подробную информацию о блокировке на интернированных строках см. В [this SO question] (http://stackoverflow.com/questions/6983714/locking-on-an-interned-string). –
Строки с одинаковым значением не обязательно совпадают памяти. Компилятор автоматически скомпилирует '' 1 "+" 2 "' как '" 12 "', но 'a +" 2 "' создается во время выполнения, приводя к другой строке, даже если она имеет такое же значение. Следовательно, вы блокируете другую переменную. – Enigmativity
@ScottChamberlain - Я не верю, что это имеет какое-либо отношение к интернированию. У вас есть ссылка на источник ссылки? – Enigmativity