Как правильно использовать Thread.Abort ()? - PullRequest
0 голосов
/ 24 апреля 2019

Что может вызвать проблемы при прерывании потока?

Мне нужно использовать Thread.Abort() в моем коде, потому что поток запускает сложный код, который имеет много циклов, объектов и условий.

Я знаю, что Thread.Abort() может привести к тупиковой ситуации при использовании Monitor, также он может предотвратить освобождение ресурсов, но я могу справиться с этими проблемами.

Я использую IDisposable / using pattern или catch ThreadAbortException, чтобы гарантировать, что все ресурсы освобождены и асинхронные операции остановлены.

Приложение теперь работает нормально. Но, поскольку код довольно сложный, я не уверен, что могут быть редкие случаи, когда прерывание потока может привести к утечкам памяти или необработанным исключениям.

Существуют ли какие-либо классы .net (например, FileStream, Dictionary), которые могут вызвать проблемы, если поток прерывается при выполнении их кода? Или какие-то другие проблемы, о которых я должен знать?

1 Ответ

1 голос
/ 24 апреля 2019

Проблема с 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 - и, таким образом, вы можете легко зайти в тупик.

...