Подтвердить что ты не робот

Вызов Thread.Abort по потоку из ThreadPool

Мой коллега использует стороннюю библиотеку .NET, для которой у нас нет исходного кода. Мы используем ThreadPool, чтобы иметь много потоков, вызываемых в эту библиотеку, и иногда один из потоков будет просто вешать навсегда, пока остальные из них весело проговаривают.

Итак, мы хотим использовать ужасный Thread.Abort, чтобы убить такие потоки. Я делал это раньше, когда вращал свои собственные потоки, но я никогда не использовал ThreadPool. Если мы отслеживаем время начала каждой задачи следующим образом:

static Dictionary<Thread, DateTime> started = new Dictionary<Thread, DateTime>();

static void DoSomeWork(object foo)
{
    lock(started)
        started[Thread.CurrentThread] = DateTime.Now;

    SomeBuggyLibraryThatMightInfiniteLoopOrSomething.callSomeFunction(doo);

    lock(started)
        started.Remove(Thread.CurrentThread);
}

тогда мы можем заблокировать и выполнить итерацию по работающим потокам и вызвать Thread.Abort, чтобы убить их? И если мы это сделаем, нам нужно добавить новый поток в ThreadPool, чтобы заменить тот, который мы только что убили, или обработать ThreadPool для нас?

РЕДАКТОР: Я очень хорошо знаю все возможные проблемы с Thread.Abort. Я знаю, что в идеале он никогда не должен использоваться в производственном коде и что он не обязательно даже останавливает поток, и что если вы прервите поток, пока поток приобретет блокировку, вы можете повесить другие потоки и т.д. Но сейчас мы находимся в сжатые сроки, и у нас есть достойные основания полагать, что в этом конкретном случае мы можем назвать Thread.Abort, не подвергая весь процесс опасности, и мы бы хотели избежать перезаписи этой программы, чтобы устранить ThreadPool, если нам абсолютно не нужно.

Итак, я хочу знать следующее: учитывая, что мы будем называть Thread.Abort в потоке, принадлежащем ThreadPool, есть ли какие-то особые проблемы, вызванные тем, что они являются потоками ThreadPool, и нам нужно вручную развернуть новый поток, чтобы заменить тот, который был убит, или ThreadPool сделает это для нас?

4b9b3361

Ответ 1

Нет, вы не должны вызывать Abort для потоков в пуле потоков. Из моего локального тестирования кажется, что ThreadPool восстанавливает потоки, если вы их прервите - я прервал 1000 потоков потока потоков, и он все еще работал. Я не знаю, следует ли вам полагаться на это поведение, но, возможно, в этом случае вы можете избежать этого. В общем случае использование Thread.Abort - неправильный способ сделать это.

Правильный способ вызова функции, которой вы не верите, чтобы вести себя хорошо, - это запустить ее в новом процессе и при необходимости убить процесс.

Ответ 2

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

Поэтому, хотя обычно не рекомендуется прерывать потоки, есть хосты, которые очень агрессивны при прерывании потоков. Один из них - ASP.NET. Когда запрос занимает слишком много времени, он прервет поток для вас. Поэтому, имея в виду это глупо сказать "никогда не прерывать потоки".

Я советую вам выяснить, где находится этот код (трассировка стека ThreadAbortException должна дать вам много информации). Если он всегда висит на одном и том же месте (это, вероятно, тупик), узнайте с помощью Reflector, если прервать поток в этот момент приведет к некоторому государственному повреждению. С этой информацией вы, возможно, уже можете исправить проблему (возможно, вы заблокируете объект этой библиотеки) или можете отправить письмо автору этой библиотеки. Если это все не помогает, и вы видите, что нет риска прервать его, быть прагматичным и убить его: -)

Однако, если происходит смена любого коррупции в государстве, вы должны попытаться ответить с ответом Марка Байера. То есть: попробуйте запустить эту библиотеку в своем собственном AppDomain. Таким образом, вы можете выгрузить полный AppDomain и никаких изменений в нем не повлияет на ваше приложение.

Ответ 3

Чтобы уточнить, что говорит Марк, если вы вызываете Thread.Abort, вы не знаете, где он будет прерван в стороннем компоненте из-за особой природы ThreadAbortException - он может оставить открытым FileStream, например.

Я лично сам создавал Threads, ссылаясь на IList или Queue (поскольку ThreadPool лучше подходит для fire и forgets или WaitHandles), и в зависимости от того, думаете ли вы отменить поток, который использует третий компонент стороны не так опасен, Abort он.

Если вы считаете, что прерывание может покинуть библиотеку третьей стороны в неприемлемом состоянии, Join потоки, которые не завершили один один, через определенное количество времени, используя System.Threading.Timer

В качестве альтернативы

Чтобы использовать ThreadPool, вы можете использовать этот Smart Thread Pool.

Ответ 4

Прочтите 'Abortable Thread Pool' Стивен Тууб. Он предоставляет исходный код для прерывистого пула потоков. Его интересное чтение.

Он вызывает свой собственный callback 'HandleItem' при очередности потока. Внутри "HandleItem" он выполняет фактический обратный вызов после добавления текущего потока в список словарей внутри своего класса-оболочки.

ThreadPool.QueueUserWorkItem(new WaitCallback(HandleItem));

HandleItem(...) {
...
_threads.Add(item, Thread.CurrentThread);
...
}

Он использует словарь для привязки своих WorkItems к Threads, когда пользователь хочет отменить поток, который он ищет, и отменяет этот конкретный поток.

Dictionary<WorkItem, Thread>() _threads = New Dictionary<WorkItem, Thread>();

http://msdn.microsoft.com/en-us/magazine/cc163644.aspx

Ответ 5

У вас есть другой вариант (который я бы взял, если бы у меня был свободный день передо мной):

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