2010-02-25 32 views
16

Mi colega está utilizando una biblioteca .NET de terceros para la que no tenemos el código fuente. Estamos usando un ThreadPool para que un montón de hilos llame a esta biblioteca, y de vez en cuando uno de los hilos quedará colgado para siempre mientras el resto de ellos avanza alegremente.Subproceso de llamada.Abordear un subproceso de un ThreadPool

Por lo tanto, queremos utilizar el temido Thread.Abort para eliminar dichos hilos. Ya he hecho esto antes al girar mis propios hilos, pero nunca he usado un ThreadPool. Si hacemos un seguimiento de las horas de inicio de cada tarea como esta:

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); 
} 

entonces podremos bloquear y iterar sobre los subprocesos que se ejecutan y llame Thread.Abort matarlos? Y si lo hacemos, ¿tendremos que agregar un hilo nuevo al ThreadPool para reemplazar el que acabamos de matar, o lo manejará el ThreadPool por nosotros?

EDIT: Estoy muy consciente de todos los problemas potenciales con Thread.Abort. Sé que lo ideal es que nunca se use en el código de producción, y que ni siquiera detiene necesariamente el hilo, y que si abortas un hilo mientras el hilo ha adquirido un bloqueo, puedes colgar otros hilos, etc. Pero ahora tenemos un plazo ajustado y tenemos razones de peso para creer que en este caso en particular, podemos llamar al Thread.Abort sin poner todo el proceso en peligro, y nos gustaría evitar volver a escribir este programa para eliminar el ThreadPool. a menos que tengamos que hacerlo absolutamente.

Entonces, lo que quiero saber es esto: dado que llamaremos al Thread.Abort en un hilo que pertenece a un ThreadPool, ¿hay algún problema especial causado por estos hilos de ThreadPool, y tenemos que hacer girar manualmente un nuevo hilo para reemplazar el que se mató o lo hará el ThreadPool por nosotros?

+0

"[Abortar un hilo ThreadPool] es peligrosa, sin embargo. En el momento en que se reciba la notificación y el hilo se interrumpe, el elemento de trabajo original, ya podría haber terminado, de modo que de la piscina el hilo ya se movió a otro elemento de trabajo . Se estaría abortando ese elemento de trabajo secundario en lugar del deseado, abriendo usted mismo a un mundo de dolor. "- Stephen Toub, MSDN Magazine 06-03 – JBSnorro

Respuesta

8

No, no debe llamar Abort on threads en el grupo de subprocesos. Según mis pruebas locales, parece que ThreadPool recreató subprocesos si los aborta: aborté 1000 subprocesos de grupo de subprocesos y aún funcionaba. No sé si deberías confiar en este comportamiento, pero tal vez puedas salirte con la tuya en este caso. En general, aunque utilizar Thread.Abort no es la forma correcta de hacerlo.

La forma correcta de llamar a una función en la que no confía para que se comporte bien es iniciarla en un nuevo proceso y matar el proceso si es necesario.

+0

¿Por qué es esto? No estoy muy familiarizado con .NET, así que ¿puedes explicar por qué los hilos de ThreadPool son diferentes de los hilos normales? Por lo que vale, he matado hilos en el pasado que cuelgan en esta biblioteca cuando no se usa un ThreadPool. Teniendo en cuenta eso, ¿hay alguna razón para creer que no funcionaría llamar a Thread.Abort en un hilo de ThreadPool aquí? (Sé que esta es una mala práctica en general, pero estamos en una fecha límite y no tenemos tiempo para reescribir mucho de esto, por lo que una solución rápida sería útil en este momento). –

+0

¿Puedes explicarlo mejor? –

+5

@Eli: Tampoco debe llamar a Thread.Abort en hilos normales. Para empezar, Thread.Abort ni siquiera garantiza la terminación del hilo. Incluso si aborta con éxito el hilo, no sabe qué desastre ha dejado atrás o cómo limpiarlo. Podría terminar con recursos no expuestos. Realmente no es una buena idea. –

5

No se aconseja el uso de Thread.Abort porque puede dejar su aplicación en un estado no válido. La razón para esto es que al exigir que se cancele un hilo, se puede hacer una excepción en casi cualquier lugar posible en esa biblioteca de terceros. Es posible que haya segmentos de código que no están escritos para poder hacer frente a estos abortos asíncronos.

Por lo tanto, aunque en general no se recomienda abortar los hilos, hay hosts que son muy agresivos al abortar los hilos. Uno de ellos es ASP.NET. Cuando una solicitud demora demasiado, abortará el hilo por usted. Entonces, con esto en mente, es tonto decir "nunca abortar hilos".

Te aconsejo que averigües dónde se cuelga este código (el seguimiento de la pila de ThreadAbortException debería darte mucha información). Si siempre se cuelga en el mismo lugar (es probable que sea un interbloqueo), averigüe con Reflector si abortar un hilo en ese punto provocará daños en el estado. Con esa información, quizás ya puedas solucionar el problema (tal vez bloquees un objeto de esa biblioteca) o puedas enviar un correo al escritor de esa biblioteca. Si todo esto no ayuda y ve que no hay riesgo de abortarlo, sea pragmático y mátelo :-)

Sin embargo, si hay un cambio de estado de corrupción, intente ir con Mark Respuesta de Byers. Es decir: intente ejecutar esa biblioteca en su propio AppDomain.De esta forma puede descargar el AppDomain completo y no hay ningún cambio que afecte su aplicación.

+0

Gracias por el análisis. Entonces, si llamo a Thread.Abort en un hilo de ThreadPool, ¿tendré que crear un nuevo hilo para reemplazarlo, o ThreadPool verá que el hilo salió y creará uno nuevo automáticamente? –

+1

Acabo de abrir Reflector para ver qué está pasando dentro de ThreadPool, pero no está claro para mí. Sospecho que los hilos abortados simplemente vuelven al grupo de subprocesos, pero no estoy seguro. No será difícil probar esto. Cree una gran cantidad de subprocesos que duerman y aborte esos y vea si el subproceso comienza a lanzar excepciones o devuelve falso. – Steven

3

Para aclarar lo que dice Mark, si llama a Thread.Abort, no tiene idea de dónde va a abortar en el componente de terceros debido a la naturaleza especial del ThreadAbortException; podría dejar un FileStream abierto, por ejemplo.

me gustaría crear personalmente los Hilos mí mismo, hace referencia en una IList o en Cola (como el ThreadPool es más adecuado para el fuego y se olvida o WaitHandles), y dependiendo de si usted piensa abortar un hilo que está utilizando el componente ISN tercera parte es peligroso, Abort.

Si usted piensa abortar puede salir de la biblioteca tercera parte en un estado inaceptable, Join los hilos que no han completado una por una, después de un período determinado de tiempo usando un System.Threading.Timer

Alternativamente

Para usar ThreadPool, puede usar este Smart Thread Pool en su lugar.

1

usted tiene otra opción (que tomaría si tuviera un día libre en frente de mí):

ingeniería inversa del componente de terceros usando reflector y de hecho fijar la llamada de bloqueo para que sea asynchroneous.

+0

Acepto que esta sería una mejor solución, pero desafortunadamente no tenemos tiempo para eso ahora. Tal vez cuando este proyecto termine. –

+1

Creo que es posible que te encuentres con una condición de carrera en el código de terceros. ¿Puedes intentar que las llamadas a esta API sean mutuamente exclusivas? –

+0

Como las llamadas a esta biblioteca desde otros hilos continúan funcionando, supongo que es un problema de red. Al igual que un zócalo se estableció para nunca tiempo de espera, o tal vez está en bucle esperando alguna entrada del protocolo de red propietario raro que utiliza. Y realmente tenemos que ejecutar al mismo tiempo muchos subprocesos al mismo tiempo, ya que estamos conectando simultáneamente a muchas computadoras diferentes en nuestra intranet corporativa. –

3

Leído 'Abortable Thread Pool' por Stephen Toub. Proporciona el código fuente para un grupo de subprocesos anulables. Es una lectura interesante

Llama a su propia devolución de llamada 'HandleItem' al poner en cola el grupo de subprocesos. Dentro de 'HandleItem', a continuación, ejecuta la devolución de llamada real, después de agregar el hilo actual a una lista de diccionario dentro de su clase contenedora.

ThreadPool.QueueUserWorkItem(new WaitCallback(HandleItem)); 

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

Se utiliza un diccionario de vincular su WorkItems a Hilos, cuando un usuario quiere cancelar un hilo que hace una mirada hacia arriba y anula ese hilo particular.

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

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

Cuestiones relacionadas