Завершение потока в C # и Thread.Abort ()

85

В MSDN описание метода Thread.Abort () гласит: «Вызов этого метода обычно завершает поток».

Почему не ВСЕГДА?

В каких случаях поток не прерывается?

Есть ли еще возможность завершить потоки?

user101375
источник

Ответы:

60

Thread.Abort()вводит ThreadAbortExceptionв поток. Поток может отменить запрос, позвонив Thread.ResetAbort(). Кроме того, есть определенные части кода, такие как finallyблок, который будет выполняться до обработки исключения. Если по какой-то причине поток застрял в таком блоке, исключение никогда не будет вызвано в потоке.

Поскольку вызывающий имеет очень слабый контроль над состоянием потока при вызове Abort(), обычно не рекомендуется делать это. Вместо этого передайте сообщение потоку с просьбой о завершении.

Брайан Расмуссен
источник
Что за сообщение? Вы имеете в виду вызов метода?
user101375
4
Это зависит от того, как вы передаете данные между потоками. Например, если ваш поток является рабочим потоком с очередью задач, вы можете поставить в очередь сообщение PleaseTerminate и позволить потоку обработать его изящно.
Брайан Расмуссен,
У моего потока есть одна большая проблема, которую необходимо решить, - это отсутствие очереди задач.
user101375
Как я уже сказал, это зависит от того, как разработан код вашего потока. Возможно, тогда вы могли бы сигнализировать через переменную-член. Трудно быть более конкретным без реального кода.
Брайан Расмуссен,
54

В каких случаях поток не прерывается?

Это дублирующийся вопрос.

Что не так с использованием Thread.Abort ()

Есть ли другие возможности для завершения потоков?

Да. Ваша проблема в том, что вы никогда не должны запускать поток, который вы не можете вежливо сказать, чтобы он остановился, и он останавливается своевременно. Если вы находитесь в ситуации, когда вам нужно запустить поток, который может быть (1) трудно остановить, (2) глючит или, что хуже всего, (3) враждебно настроен по отношению к пользователю, то правильным решением будет сделать новый процесс, запустите поток в новом процессе, а затем завершите процесс, когда вы хотите, чтобы поток остановился. Единственное, что может гарантировать безопасное завершение некооперативного потока, - это остановка всего процесса операционной системой.

Подробнее см. Мой слишком длинный ответ на этот вопрос:

Использование оператора блокировки в цикле в C #

Соответствующий бит - это бит в конце, где я обсуждаю, какие соображения касаются того, как долго вы должны ждать, пока поток не уничтожит себя, прежде чем вы его прервете.

Эрик Липперт
источник
Эрик, как мне вежливо сказать потоку остановиться, если этот поток ожидает объекта синхронизации, например EventWaitHandle.WaitOne () ;?
Корвин
5
@Corvin: контракт между двумя потоками, описывающий, как один поток должен взаимодействовать с другим, зависит от авторов кода, который выполняется в этих потоках. Если у вас есть требования, согласно которым (1) поток X должен ждать объекта потенциально вечно и (2) этот поток Y должен иметь возможность полностью завершить поток X за конечный промежуток времени, тогда, я думаю, у вас есть противоречивые требования; решить, кто из них выиграет. В первом случае потоку Y придется ждать. В последнем случае поток X не должен ждать вечно, он должен ждать некоторое время.
Эрик Липперт
Спасибо за ответы. Рассмотрим следующую архитектуру: у меня есть приложение, которое должно минимизировать себя при возникновении определенного общесистемного события. У меня есть поток в этом приложении, который ожидает глобального именованного события и выполняет свою работу, когда это событие установлено. С другой стороны, моему приложению может потребоваться завершить работу до того, как произойдет событие, и придется закрыть этот ожидающий поток. Как самураи кодируют такую ​​вещь?
Корвин,
@Corvin: У вас может быть цикл, который ожидает (с коротким таймаутом) события, а затем проверяет, следует ли прекратить попытки ожидания (чтобы он мог нормально выйти). Таким образом, вы можете сигнализировать своему потоку, что он должен остановиться, и вам нужно только ждать, пока ваш таймаут не остановится.
Кевин Киблер
2
@Corvin, если вы сделаете этот ожидающий поток фоновым потоком, вам не нужно закрывать его до выхода из приложения.
Дон Киркби
17

Почему не ВСЕГДА? В каких случаях он не прерывает обсуждение?

Во-первых, поток может поймать a ThreadAbortExceptionи отменить собственное завершение. Или он может выполнить вычисление, которое займет вечность, пока вы пытаетесь его прервать. Из-за этого среда выполнения не может гарантировать, что поток всегда завершится после того, как вы его попросите.

ThreadAbortException имеет больше:

Когда выполняется вызов метода Abort для уничтожения потока, среда CLR генерирует исключение ThreadAbortException. ThreadAbortException - это особое исключение, которое может быть перехвачено, но оно будет автоматически вызвано снова в конце блока перехвата. Когда возникает это исключение, среда выполнения выполняет все блоки finally перед завершением потока. Поскольку поток может выполнять неограниченные вычисления в блоках finally или вызывать Thread.ResetAbort()отмену прерывания, нет гарантии, что поток когда-либо завершится.

Вам не нужно создавать Abort()поток вручную. CLR сделает за вас всю грязную работу, если вы просто позволите методу в потоке вернуться; это нормально завершит поток.

Джон Феминелла
источник
Мне нужно Abort (), потому что в этом потоке запущен цикл сообщений (Dispatcher.Run ();). Если я правильно понимаю, мне нужно вызвать метод, вызывающий возврат в поток, чтобы завершить его?
user101375
7

FileStream.Read()к именованному каналу, который в настоящее время ничего не получает (читает блоки вызовов во время ожидания входящих данных), не отвечает Thread.Abort(). Остается внутри Read()звонка.

веселый
источник
6

Что делать, если поток удерживает блокировку и прерывается / прекращается? Ресурсы остаются застрявшими

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

ссылка MSDN


см. Рекомендации по управлению потоками

Асад
источник
5

Кажется, я не могу прервать поток, который застрял в цикле:

//immortal
Thread th1 = new Thread(() => { while (true) {}});

Однако я могу прервать поток, если во время цикла спит:

//mortal
Thread th2 = new Thread(() => { while (true) { Thread.Sleep(1000); }});
Колин
источник
1
это не похоже на правду, я запустил тестовую программу: `{var th = new Thread (() => {int i = 0; try {while (true) {i ++;}} catch (Exception e) { Console.WriteLine ("aborting @ {0}", i);}}); th.Start (); Thread.Sleep (1000); th.Abort (); th.Join (); }
Weipeng L
3

Потому что вы можете поймать ThreadAbortExceptionи вызвать Thread.ResetAbortвнутри обработчика.

Эриккаллен
источник
0

ОТ: Подробный, не зависящий от языка, сомнительно полезный и чертовски забавный взгляд на параллелизм см. В Verity Stob !

Pontus Gagge
источник
-1

У меня были случаи, когда поток был слишком занят, чтобы услышать вызов Abort (), что обычно приводит к тому, что мой код генерирует исключение ThreadAbortingException.

Энди Шеллэм
источник