Я создал поток, выполняющий определенный метод. Но иногда я хотел бы убить поток, даже если он все еще работает. Как я могу это сделать? Я попробовал Thread.Abort(), но он показывает окно сообщения с надписью «Поток прерван». Что я должен делать?
Уничтожение потока .NET
Ответы (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. */
}
select()
в Unix.
- person Jonathon Reinhart; 12.11.2013
Вы действительно должны вызывать 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 мс), и поток снова пройдет через цикл.
while (!WillTerminate) {..} HasTerminate.Set();
- person mathk; 30.07.2012
DoWork
BackgroundWorker
?
- person Ehsan Sajjad; 14.04.2016
Нехорошо убивать нить. Лучше подать сигнал, чтобы он остановился, и дать ему закончиться изящно. Существуют различные способы сделать это.
- Используйте
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#1752948
Я бы сказал, что 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( );
}
}
await
будет продолжать выполняться в том же потоке, что и до ожидания. В вашем примере точка await Task.Delay(1)
— это место, где _thread
завершится, а последующая Thread.Sleep(FromMinutes(10))
усыпит поток ThreadPool
вместо _thread
. И тогда _thread.Interrupt();
ничего не сделает.
- person Theodor Zoulias; 17.07.2021
Console
Thread.CurrentThread.ManagedThreadId
до и после await
и посмотрите, совпадают ли они или нет.
- person Theodor Zoulias; 17.07.2021