Уничтожение потока.NET
Я создал поток, выполняющий определенный метод. Но иногда я хотел бы убить поток, даже если он все еще работает. Как я могу это сделать? Я попытался Thread.Abort(), но он отображает окно с сообщением "Тема прервана". Что я должен делать?
8 ответов
Не звониThread.Abort()
!
Thread.Abort
опасный. Вместо этого вы должны сотрудничать с потоком, чтобы его можно было спокойно отключить. Поток должен быть спроектирован так, чтобы ему можно было убить себя, например, с помощью логического значенияkeepGoing
флаг, установленный в false, когда вы хотите остановить поток. Тогда поток будет иметь что-то вроде
while (keepGoing)
{
/* Do work. */
}
Если поток может заблокировать в Sleep
или жеWait
тогда вы можете вырвать его из этих функций, вызвавThread.Interrupt()
, Затем следует подготовить нить для обработки ThreadInterruptedException
:
try
{
while (keepGoing)
{
/* Do work. */
}
}
catch (ThreadInterruptedException exception)
{
/* Clean up. */
}
Вы должны действительно вызывать Abort() только в крайнем случае. Вместо этого вы можете использовать переменную для синхронизации этого потока:
volatile bool shutdown = false;
void RunThread()
{
while (!shutdown)
{
...
}
}
void StopThread()
{
shutdown = true;
}
Это позволяет вашему потоку полностью завершить то, что он делал, оставляя ваше приложение в известном хорошем состоянии.
Самый правильный и потокобезопасный способ - использовать WaitHandle для подачи сигнала потоку, когда он должен остановиться. Я в основном использую ManualResetEvent.
В вашей теме вы можете иметь:
private void RunThread()
{
while(!this.flag.WaitOne(TimeSpan.FromMilliseconds(100)))
{
// ...
}
}
где this.flag
является экземпляром ManualResetEvent. Это означает, что вы можете позвонить this.flag.Set()
извне, чтобы остановить цикл.
Метод WaitOne будет возвращать true только при установленном флаге. В противном случае он истечет через указанное время (в примере 100 мс), и поток снова пройдет цикл.
Это не очень хорошая идея, чтобы убить поток. Лучше сигнализировать, что это должно остановиться и позволить этому закончиться изящно. Существуют различные способы сделать это.
- использование
Thread.Interrupt
тыкать его, если он заблокирован. - Опрос переменной флага.
- Использовать
WaitHandle
класс для отправки сигнала.
Мне не нужно перефразировать, как можно использовать каждый метод, так как я уже сделал это в этом ответе.
Отмена потока - очень плохая идея, поскольку вы не можете определить, что поток делал во время прерывания.
Вместо этого имейте свойство, которое поток может проверить, и который может установить ваш внешний код. Пусть поток проверяет это логическое свойство, когда он находится в безопасном месте для выхода.
Я согласен с Джоном Б.
volatile bool shutdown = false;
void RunThread()
{
try
{
while (!shutdown)
{
/* Do work. */
}
}
catch (ThreadAbortException exception)
{
/* Clean up. */
}
}
void StopThread()
{
shutdown = true;
}
Есть также примеры уничтожения тем в моем классе WebServer...
https://net7ntcip.codeplex.com/SourceControl/changeset/view/89621
Я бы сказал, что с Abort все в порядке, просто поймите, каковы последствия... пока вы указываете состояние до того, как продолжительная задача Abort будет работать, но требуются флаги, такие как (ShouldStop или ActionBranch и т. Д.)
Проверьте это на примерах!
Редактировать:
Я создал небольшой класс для этого
Заметил, что это не работает с async/await
Опубликовал обновление в классе, затем Теодор Зулиас (спасибо :)) сообщил мне, что эта идея ошибочна.
Теперь я публикую исходный класс (не работает с async/await!)
var t = new StopAbleThread(isShutDown => { Console.WriteLine("a"); while (!isShutDown()) { Console.WriteLine("b"); Thread.Sleep(TimeSpan.FromMinutes(10)); } } ) { IsBackground = true }; t.Start( );
Остановить ветку так:
t.Stop();
Класс:
public class StopAbleThread
{
public bool IsBackground
{
set => _thread.IsBackground = value;
}
private readonly Thread _thread;
private volatile bool _shutdown;
public delegate bool IsShutDown( );
public delegate void CodeToRun( IsShutDown isShutDown );
public StopAbleThread( CodeToRun codeToRun )
{
_thread = new Thread( ( ) =>
{
try
{
codeToRun( _IsShutDown );
}
catch( ThreadInterruptedException )
{
//ignore
}
} );
}
private bool _IsShutDown( )
{
return _shutdown;
}
public void Start( )
{
_thread.Start( );
}
public void Stop( )
{
_shutdown = true;
_thread.Interrupt( );
}
}