Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Как убить поток с бесконечным циклом и обработкой ThreadAbortException

#c_sharp


Есть ли способ убить поток, в котором отрабатывает такой метод? 

public static void M()
{
    while (true)
    {
        try
        {

        }
        catch (ThreadAbortException)
        {
            Thread.ResetAbort();
        }
    }
}

    


Ответы

Ответ 1



Можно воспользоваться атрибутом SecurityPermission. Например, при таком подходе происходит облом на попытке выполнить Thread.ResetAbort(); static void Main(string[] args) { var thread = new Thread(SomeMethod); thread.Start(); Thread.Sleep(5000); thread.Abort(); while (thread.IsAlive) { } Console.WriteLine("Готово"); Console.ReadKey(); } [SecurityPermission(SecurityAction.PermitOnly, ControlThread = false)] public static void SomeMethod() { while (true) { try { Thread.Sleep(1000); Console.WriteLine("1"); } catch (Exception e) { Thread.ResetAbort(); } } } Однако, смерть потока с необработанным исключением означает смерть всего приложения => нужно обернуть все в глобальный try/catch вокруг метода из ненадежного источника. А вот такой поток не умирает: static void Main(string[] args) { var thread = new Thread(SomeMethod); thread.Start(); Thread.Sleep(2000); thread.Abort(); while (thread.IsAlive) { Thread.Sleep(2000); thread.Abort(); } Console.WriteLine("Готово"); Console.ReadKey(); } [SecurityPermission(SecurityAction.PermitOnly, ControlThread = false)] public static void SomeMethod() { try { while (true) { try { Thread.Sleep(1000); Console.WriteLine("1"); } catch (Exception e) { try { Thread.ResetAbort(); } catch (Exception exception) { while (true) { Thread.Sleep(1000); Console.WriteLine("2"); } } } } } catch (Exception e) { Console.WriteLine(e); } } Делая ResetAbort поток обламывается, но обрабатывает исключение и крутится в бесконечном цикле. Многократные попытки вызвать Abort из главного потока не срабатывают. Поток продолжает жить... Хотя, есть другое более правильное решение- домены приложений (AppDomain), который как раз используются для кода из ненадежного источника. Если мы загрузим небезопасный код в отдельный домен, то он успешно сможет прерваться при выгрузке домена, чихая на Thread.ResetAbort();: public class TestClass : MarshalByRefObject { public void SomeMethod() { while (true) { try { Thread.Sleep(1000); Console.WriteLine("1"); } catch (Exception e) { try { Thread.ResetAbort(); } catch (Exception exception) { while (true) { Thread.Sleep(1000); Console.WriteLine("2"); } } } } } } class Program { static void Main(string[] args) { AppDomain domain = AppDomain.CreateDomain("MyDomain"); var t=(TestClass)domain.CreateInstanceAndUnwrap(typeof(TestClass).Assembly.FullName, typeof(TestClass).FullName); Task.Run(() => t.SomeMethod()); Thread.Sleep(2000); AppDomain.Unload(domain); Console.ReadKey(); } } Если верить Рихтеру, то по такому принципу работает механизм SQL CLR в MS SQL SERVER, который не дает сборке вызвать Thread.RestAbort().

Ответ 2



Способ принудительно завершить поток есть, хотя MS его не рекомендует как слишком грубый. Это функция WinAPI TerminateThread. В качестве параметра она требует хэндл потока, его можно получить сначала узнав ID потока через ManagedThreadId, а потом по ID получить хэндл функцией WinAPI OpenThread.

Комментариев нет:

Отправить комментарий