Сегодня вечером я играл с областями ограниченного выполнения, чтобы лучше понять мое понимание из более мелких деталей. Я использовал их и раньше, но в основном строго придерживался установленных шаблонов. Так или иначе, я заметил нечто странное, что не могу объяснить.
Рассмотрим следующий код. Обратите внимание, что я нацелился на .NET 4.5 и протестировал его с помощью сборки Release без подключенного отладчика.
public class Program
{
public static void Main(string[] args)
{
bool toggle = false;
bool didfinally = false;
var thread = new Thread(
() =>
{
Console.WriteLine("running");
RuntimeHelpers.PrepareConstrainedRegions();
try
{
while (true)
{
toggle = !toggle;
}
}
finally
{
didfinally = true;
}
});
thread.Start();
Console.WriteLine("sleeping");
Thread.Sleep(1000);
Console.WriteLine("aborting");
thread.Abort();
Console.WriteLine("aborted");
thread.Join();
Console.WriteLine("joined");
Console.WriteLine("didfinally=" + didfinally);
Console.Read();
}
}
Как вы думаете, что будет на выходе этой программы?
- сделал наконец=Истина
- didfinally=ложь
Прежде чем гадать, прочтите документацию. Я включаю соответствующие разделы ниже.
Ограниченная область выполнения (CER) является частью механизма создания надежного управляемого кода. CER определяет область, в которой общеязыковая среда выполнения (CLR) не может создавать внеполосные исключения, которые могут препятствовать выполнению кода в этой области в полном объеме. В этом регионе пользовательский код не может выполнять код, который может привести к возникновению внештатных исключений. Метод PrepareConstrainedRegions должен непосредственно предшествовать блоку try и помечать блоки catch, наконец, и блоки сбоя как ограниченные области выполнения. После того, как код помечен как ограниченная область, он должен вызывать только другой код со строгими контрактами надежности, а код не следует выделять или выполнять виртуальные вызовы неподготовленных или ненадежных методов, если код не подготовлен для обработки сбоев. CLR задерживает прерывание потока для кода, который выполняется в CER.
и
Надежность try/catch/finally — это механизм обработки исключений с тем же уровнем гарантий предсказуемости, что и в неуправляемой версии. Блок catch/finally — это CER. Методы в блоке требуют предварительной подготовки и не должны прерываться.
Сейчас меня особенно беспокоит защита от прерывания потока. Есть два типа: ваш обычный вариант через Thread.Abort
, а затем тот, где хост CLR может пойти на вас со всеми средневековья и сделать принудительное прерывание. Блоки finally
уже в какой-то степени защищены от Thread.Abort
. Затем, если вы объявите этот блок finally
как CER, вы также получите дополнительную защиту от прерываний хоста CLR ... по крайней мере, я думаю, что это теория.
Итак, основываясь на том, что, как я думаю, я знаю, я догадался № 1. Он должен напечатать didfinally=True. ThreadAbortException
вводится, пока код все еще находится в блоке try
, а затем CLR позволяет блоку finally
работать, как и ожидалось, даже без CER, верно?
Ну, это не тот результат, который я получил. Я получил совершенно неожиданный результат. Ни №1, ни №2 у меня не получилось. Вместо этого моя программа зависла на Thread.Abort
. Вот что я наблюдаю.
- Наличие
PrepareConstrainedRegions
задерживает прерывание потока внутриtry
блоков. - Отсутствие
PrepareConstrainedRegions
позволяет использовать их вtry
блоках.
Итак, вопрос на миллион долларов: почему? Документация нигде не упоминает об этом поведении, насколько я могу видеть. На самом деле, большая часть того, что я читаю, на самом деле предлагает вам поместить критический непрерываемый код в блок finally
специально для защиты от прерывания потока.
Возможно, PrepareConstrainedRegions
задерживает нормальное прерывание в блоке try
в дополнение к блоку finally
. Но прерывания хоста CLR задерживаются только в блоке finally
CER? Может ли кто-нибудь дать больше ясности по этому поводу?