C # Припинення потоку та Thread.Abort ()


85

У MSDN в описі методу Thread.Abort () сказано: "Виклик цього методу зазвичай завершує потік".

Чому б НЕ ЗАВЖДИ?

У яких випадках це не припиняє потік?

Чи є якась інша можливість припинення потоків?

Відповіді:


60

Thread.Abort()вводить a 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

Чому б НЕ ЗАВЖДИ? У яких випадках це не стосується нитки?

Для початку, потік може зачепити ThreadAbortExceptionі скасувати власне завершення. Або він може виконати обчислення, яке триває вічно, поки ви намагаєтесь його перервати. Через це час виконання не може гарантувати, що потік завжди закінчується після того, як ви про це попросите.

ThreadAbortException має більше:

Коли здійснюється виклик методу Abort для знищення потоку, загальновиконавча програма виконує ThreadAbortException. ThreadAbortException - це особливий виняток, який можна перехопити, але він буде автоматично піднятий знову в кінці блоку catch. Коли цей виняток викликаний, час виконання виконує всі нарешті блоки перед закінченням потоку. Оскільки потік може здійснити необмежене обчислення в блоках нарешті або викликати Thread.ResetAbort()скасування переривання, немає гарантії, що потік коли-небудь закінчиться.

Вам не потрібно Abort()вручну обробляти потоки. CLR зробить за вас всю брудну роботу, якщо ви просто дозволите методу в потоці повернутися; що закінчить потік нормально.


Мені потрібно перервати () це через цикл повідомлень, запущений у цьому потоці (Dispatcher.Run ();). Якщо я правильно розумію, мені потрібно викликати метод, що викликає return, у потік, щоб закінчити його?
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 ("переривання @ {0}", i);}}); th.Start (); Thread.Sleep (1000); th.Abort (); th.Приєднатися (); }
Weipeng L


3

Оскільки ви можете зловити ThreadAbortExceptionі викликати Thread.ResetAbortвсередині обробника.


0

ВІД: Щодо всебічного, мовного агностику, сумнівно корисного та смішного сприйняття паралелізму, див. Verity Stob !


-1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.