Проблема с Thread.Abort
состоит в том, что ваш ThreadAbortException
может быть брошен между любыми двумя инструкциями (почти).
Если вы берете какой-то очень простой код, такой как:
public void M()
{
using (CreateThing())
{
}
}
public IDisposable CreateThing() => null;
И посмотрите на сгенерированный C # и IL :
public void M()
{
IDisposable disposable = CreateThing();
try
{
}
finally
{
if (disposable != null)
{
disposable.Dispose();
}
}
}
Вы можете видеть, что есть пара инструкций между вызываемым CreateThing
и входом вtry
блок.Есть небольшое окно возможностей, где, если Thread.Abort
вызывается именно тогда, ваш объект будет не удален.
Так что использование IDisposable
и using
не не гарантирует, что ваши ресурсы будут освобождены перед лицом Thread.Abort
.
Существует очень веская причина, по которой Thread.Abort
был удален из .NET Standard, и почему вы должны использовать CancellationToken
.
Вы должны использовать CancellationToken
, и ваш код должен проверить, был ли он отменен (используя CancellationToken.ThrowIfCancellationRequested()
) в подходящих, безопасных точках.
За исключением, lock
операторы используют перегрузку Monitor.Enter
, которая возвращает логическое значение, указывающее, была ли блокировка фактически получена, и:
lock (lockObject)
{
}
компилируется в:
bool lockTaken = false;
try
{
Monitor.Enter(lockObject, ref lockTaken);
}
finally
{
if (lockTaken)
Monitor.Exit(lockObject);
}
вИзбегайте именно этой проблемы.
Однако вы не получите такой роскоши при использовании любых других методов синхронизации - только lock
- и, таким образом, вы можете легко зайти в тупик.