#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.
Комментариев нет:
Отправить комментарий