No puedes Las tareas usan subprocesos de fondo del grupo de subprocesos. Tampoco se recomienda cancelar hilos usando el método Abortar. Puede echar un vistazo a la siguiente publicación de blog que explica una forma adecuada de cancelar tareas utilizando tokens de cancelación. Aquí hay un ejemplo:
classProgram{staticvoidMain(){var ts =newCancellationTokenSource();CancellationToken ct = ts.Token;Task.Factory.StartNew(()=>{while(true){// do some heavy work hereThread.Sleep(100);if(ct.IsCancellationRequested){// another thread decided to cancelConsole.WriteLine("task canceled");break;}}}, ct);// Simulate waiting 3s for the task to completeThread.Sleep(3000);// Can't wait anymore => cancel this task
ts.Cancel();Console.ReadLine();}}
Buena explicación Tengo una pregunta, ¿cómo funciona cuando no tenemos un método anónimo en Task.Factory.StartNew? como Task.Factory.StartNew (() => ProcessMyMethod (), cancellationToken)
Prerak K
61
¿Qué pasa si hay una llamada de bloqueo que no regresa dentro de la tarea de ejecución?
mehmet6parmak
3
@ mehmet6parmak Creo que lo único que puede hacer es usarlo Task.Wait(TimeSpan / int)para darle una fecha límite (basada en el tiempo) desde el exterior.
Mark
2
¿Qué sucede si tengo mi clase personalizada para administrar la ejecución de métodos dentro de una nueva Task? Algo así como: public int StartNewTask(Action method). Dentro del StartNewTaskmétodo se crea una nueva Taskpor: Task task = new Task(() => method()); task.Start();. Entonces, ¿cómo puedo gestionar el CancellationToken? También me gustaría saber si Threadnecesito implementar una lógica para verificar si hay algunas Tareas que aún están pendientes y así matarlas cuando Form.Closing. Con Threadslo uso Thread.Abort().
Cheshire Cat
¡Oh, qué mal ejemplo! Es una condición booleana simple, por supuesto, ¡es lo primero que se intentaría! Pero es decir, tengo una función en una tarea separada, que puede tardar mucho tiempo en finalizar, e idealmente no debería saber nada sobre un subproceso o lo que sea. Entonces, ¿cómo cancelo la función con su consejo?
Hola Ángel
32
Anular una tarea es fácilmente posible si captura el hilo en el que se ejecuta la tarea. Aquí hay un código de ejemplo para demostrar esto:
voidMain(){Thread thread =null;Task t =Task.Run(()=>{//Capture the thread
thread =Thread.CurrentThread;//Simulate work (usually from 3rd party code)Thread.Sleep(1000);//If you comment out thread.Abort(), then this will be displayedConsole.WriteLine("Task finished!");});//This is needed in the example to avoid thread being still NULLThread.Sleep(10);//Cancel the task by aborting the thread
thread.Abort();}
Usé Task.Run () para mostrar el caso de uso más común para esto: usar la comodidad de Tareas con código antiguo de subproceso único, que no usa la clase CancellationTokenSource para determinar si debe cancelarse o no.
Gracias por esta idea Usé este enfoque para implementar un tiempo de espera para algún código externo, que no tiene CancellationTokensoporte ...
Christoph Fink
77
AFAIK thread.abort te dejará desconocido sobre tu pila, puede ser inválido. Nunca lo he intentado, pero supongo que comenzar un hilo en un dominio de aplicación separado que thread.abort se guardará. Además, se desperdicia un hilo completo en su solución solo para abortar una tarea. No tendría que usar tareas sino hilos en primer lugar. (
voto negativo
1
Como escribí, esta solución es un último recurso que podría considerarse bajo ciertas circunstancias. Por supuesto, se CancellationTokendeben considerar soluciones simples o incluso más simples que estén libres de condiciones de carrera. El código anterior solo ilustra el método, no el área de uso.
Florian Rappl
8
Creo que este enfoque podría tener consecuencias desconocidas y no lo recomendaría en el código de producción. No siempre se garantiza que las tareas se ejecuten en un subproceso diferente, lo que significa que se pueden ejecutar en el mismo subproceso que el que las creó si el planificador lo decide (lo que significa que el subproceso principal se pasará a la threadvariable local). En su código, puede terminar abortando el hilo principal, que no es lo que realmente quiere. Quizás sea una buena
idea
10
@ Martin: Al investigar esta pregunta en SO, veo que ha rechazado varias respuestas que usan Thread.Abort para eliminar tareas, pero no ha proporcionado ninguna solución alternativa. ¿Cómo puede eliminar el código de terceros que no admite la cancelación y se ejecuta en una tarea ?
Gordon Bean
32
Como sugiere esta publicación , esto se puede hacer de la siguiente manera:
intFoo(CancellationToken token){Thread t =Thread.CurrentThread;
using (token.Register(t.Abort)){// compute-bound work here}}
Aunque funciona, no se recomienda utilizar este enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que se encargue de la cancelación adecuada.
+1 por dar un enfoque diferente mientras declara sus retrocesos. No sabía que esto podría hacerse :)
Joel
1
Gracias por la solucion! Simplemente podemos pasar el token al método y cancelar el tokensource, en lugar de obtener de alguna manera una instancia de hilo del método y abortar esta instancia directamente.
Este tipo de cosas es una de las razones logísticas por las que Abortestá en desuso. En primer lugar, no use Thread.Abort()para cancelar o detener un hilo si es posible. Abort()solo debe usarse para matar a la fuerza un hilo que no responde a solicitudes más pacíficas para detenerse de manera oportuna.
Dicho esto, debe proporcionar un indicador de cancelación compartido de que un subproceso se establece y espera mientras el otro subproceso verifica periódicamente y sale correctamente. .NET 4 incluye una estructura diseñada específicamente para este propósito, el CancellationToken.
No debe intentar hacer esto directamente. Diseñe sus tareas para trabajar con un CancellationToken y cancélelas de esta manera.
Además, recomendaría cambiar su hilo principal para que funcione a través de un CancellationToken también. Llamar Thread.Abort()es una mala idea: puede provocar varios problemas que son muy difíciles de diagnosticar. En cambio, ese hilo puede usar la misma Cancelación que usan sus tareas, y lo mismo CancellationTokenSourcepuede usarse para desencadenar la cancelación de todas sus tareas y su hilo principal.
Esto conducirá a un diseño mucho más simple y seguro.
Para responder la pregunta de Prerak K sobre cómo usar CancellationTokens cuando no se usa un método anónimo en Task.Factory.StartNew (), se pasa el CancellationToken como parámetro al método que está comenzando con StartNew (), como se muestra en el ejemplo de MSDN aquí .
p.ej
var tokenSource =newCancellationTokenSource();var token = tokenSource.Token;Task.Factory.StartNew(()=>DoSomeWork(1, token), token);staticvoidDoSomeWork(int taskNum,CancellationToken ct){// Do work here, checking and acting on ct.IsCancellationRequested where applicable, }
En primer lugar, estoy tratando de cancelarlo cortésmente con el uso de la cancelación .
Si todavía se está ejecutando (por ejemplo, debido a un error del desarrollador), entonces compórtate mal y mátalo usando un método de cancelación de la vieja escuela .
Vea un ejemplo a continuación:
privateCancellationTokenSource taskToken;privateAutoResetEvent awaitReplyOnRequestEvent =newAutoResetEvent(false);voidMain(){// Start a task which is doing nothing but sleeps 1sLaunchTaskAsync();Thread.Sleep(100);// Stop the taskStopTask();}/// <summary>/// Launch task in a new thread/// </summary>voidLaunchTaskAsync(){
taskToken =newCancellationTokenSource();Task.Factory.StartNew(()=>{try{//Capture the thread
runningTaskThread =Thread.CurrentThread;// Run the taskif(taskToken.IsCancellationRequested||!awaitReplyOnRequestEvent.WaitOne(10000))return;Console.WriteLine("Task finished!");}catch(Exception exc){// Handle exception}}, taskToken.Token);}/// <summary>/// Stop running task/// </summary>voidStopTask(){// Attempt to cancel the task politelyif(taskToken !=null){if(taskToken.IsCancellationRequested)return;else
taskToken.Cancel();}// Notify a waiting thread that an event has occurredif(awaitReplyOnRequestEvent !=null)
awaitReplyOnRequestEvent.Set();// If 1 sec later the task is still running, kill it cruellyif(runningTaskThread !=null){try{
runningTaskThread.Join(TimeSpan.FromSeconds(1));}catch(Exception ex){
runningTaskThread.Abort();}}}
Las tareas tienen soporte de primera clase para la cancelación a través de tokens de cancelación . Cree sus tareas con tokens de cancelación y cancele las tareas a través de estos explícitamente.
Puede usar a CancellationTokenpara controlar si la tarea se cancela. ¿Estás hablando de abortarlo antes de que comience ("no importa, ya hice esto") o de interrumpirlo en el medio? Si es lo primero, CancellationTokenpuede ser útil; si es lo último, probablemente necesitará implementar su propio mecanismo de "rescate" y verificar en los puntos apropiados en la ejecución de la tarea si debe fallar rápidamente (aún puede usar CancellationToken para ayudarlo, pero es un poco más manual).
Las tareas se ejecutan en ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que abortar el hilo no puede afectar las tareas. Para abortar tareas, vea Cancelación de tareas en msdn.
Lo intenté CancellationTokenSourcepero no puedo hacer esto. Y lo hice a mi manera. Y funciona.
namespace Blokick.Provider{publicclassSignalRConnectProvider{publicSignalRConnectProvider(){}publicboolIsStopRequested{get;set;}=false;//1-)This is important and default `false`.publicasyncTask<string>ConnectTab(){string messageText ="";for(int count =1; count <20; count++){if(count ==1){//Do stuff.}try{//Do stuff.}catch(Exception ex){//Do stuff.}if(IsStopRequested)//3-)This is important. The control of the task stopping request. Must be true and in inside.{return messageText ="Task stopped.";//4-) And so return and exit the code and task.}if(Connected){//Do stuff.}if(count ==19){//Do stuff.}}return messageText;}}}
Y otra clase de llamar al método:
namespace Blokick.Views{[XamlCompilation(XamlCompilationOptions.Compile)]publicpartialclassMessagePerson:ContentPage{SignalRConnectProvider signalR =newSignalRConnectProvider();publicMessagePerson(){InitializeComponent();
signalR.IsStopRequested=true;// 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.if(signalR.ChatHubProxy!=null){
signalR.Disconnect();}LoadSignalRMessage();}}}
Puede abortar una tarea como un hilo si puede hacer que la tarea se cree en su propio hilo y llamar Aborta su Threadobjeto. De forma predeterminada, una tarea se ejecuta en un subproceso de grupo de subprocesos o en el subproceso de llamada, ninguno de los cuales normalmente desea anular.
Para garantizar que la tarea tenga su propio hilo, cree un planificador personalizado derivado de TaskScheduler. En su implementación de QueueTask, cree un nuevo hilo y úselo para ejecutar la tarea. Más tarde, puede abortar el subproceso, lo que hará que la tarea se complete en un estado defectuoso con un ThreadAbortException.
El Thread.Abortmétodo debe usarse con precaución. Particularmente cuando lo llama para abortar un hilo que no sea el hilo actual, no sabe qué código se ha ejecutado o no se pudo ejecutar cuando se lanza ThreadAbortException , ni puede estar seguro del estado de su aplicación o de cualquier aplicación y estado de usuario que es responsable de preservar. Por ejemplo, llamar Thread.Abortpuede evitar que los constructores estáticos se ejecuten o evitar la liberación de recursos no administrados.
Este código falla con una excepción de tiempo de ejecución: System.InvalidOperationException: RunSynchronously no se puede invocar en una tarea que ya se inició.
Theodor Zoulias
1
@TheodorZoulias Buena captura. Gracias. Arreglé el código y, en general, mejoré la respuesta.
Edward Brey
1
Sí, esto solucionó el error. Otra advertencia de que probablemente debería mencionarse es que Thread.Abortno es compatible con .NET Core. Intentar usarlo da como resultado una excepción: System.PlatformNotSupportedException: Thread abort no es compatible con esta plataforma. Una tercera advertencia es que SingleThreadTaskSchedulerno se puede usar de manera efectiva con tareas de estilo prometedor, en otras palabras, con tareas creadas con asyncdelegados. Por ejemplo, un incrustado se await Task.Delay(1000)ejecuta en ningún subproceso, por lo que no se ve afectado por los eventos de subproceso.
Respuestas:
No puedes Las tareas usan subprocesos de fondo del grupo de subprocesos. Tampoco se recomienda cancelar hilos usando el método Abortar. Puede echar un vistazo a la siguiente publicación de blog que explica una forma adecuada de cancelar tareas utilizando tokens de cancelación. Aquí hay un ejemplo:
fuente
Task.Wait(TimeSpan / int)
para darle una fecha límite (basada en el tiempo) desde el exterior.Task
? Algo así como:public int StartNewTask(Action method)
. Dentro delStartNewTask
método se crea una nuevaTask
por:Task task = new Task(() => method()); task.Start();
. Entonces, ¿cómo puedo gestionar elCancellationToken
? También me gustaría saber siThread
necesito implementar una lógica para verificar si hay algunas Tareas que aún están pendientes y así matarlas cuandoForm.Closing
. ConThreads
lo usoThread.Abort()
.Anular una tarea es fácilmente posible si captura el hilo en el que se ejecuta la tarea. Aquí hay un código de ejemplo para demostrar esto:
Usé Task.Run () para mostrar el caso de uso más común para esto: usar la comodidad de Tareas con código antiguo de subproceso único, que no usa la clase CancellationTokenSource para determinar si debe cancelarse o no.
fuente
CancellationToken
soporte ...CancellationToken
deben considerar soluciones simples o incluso más simples que estén libres de condiciones de carrera. El código anterior solo ilustra el método, no el área de uso.thread
variable local). En su código, puede terminar abortando el hilo principal, que no es lo que realmente quiere. Quizás sea una buenaComo sugiere esta publicación , esto se puede hacer de la siguiente manera:
Aunque funciona, no se recomienda utilizar este enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que se encargue de la cancelación adecuada.
fuente
Este tipo de cosas es una de las razones logísticas por las que
Abort
está en desuso. En primer lugar, no useThread.Abort()
para cancelar o detener un hilo si es posible.Abort()
solo debe usarse para matar a la fuerza un hilo que no responde a solicitudes más pacíficas para detenerse de manera oportuna.Dicho esto, debe proporcionar un indicador de cancelación compartido de que un subproceso se establece y espera mientras el otro subproceso verifica periódicamente y sale correctamente. .NET 4 incluye una estructura diseñada específicamente para este propósito, el
CancellationToken
.fuente
No debe intentar hacer esto directamente. Diseñe sus tareas para trabajar con un CancellationToken y cancélelas de esta manera.
Además, recomendaría cambiar su hilo principal para que funcione a través de un CancellationToken también. Llamar
Thread.Abort()
es una mala idea: puede provocar varios problemas que son muy difíciles de diagnosticar. En cambio, ese hilo puede usar la misma Cancelación que usan sus tareas, y lo mismoCancellationTokenSource
puede usarse para desencadenar la cancelación de todas sus tareas y su hilo principal.Esto conducirá a un diseño mucho más simple y seguro.
fuente
Para responder la pregunta de Prerak K sobre cómo usar CancellationTokens cuando no se usa un método anónimo en Task.Factory.StartNew (), se pasa el CancellationToken como parámetro al método que está comenzando con StartNew (), como se muestra en el ejemplo de MSDN aquí .
p.ej
fuente
Utilizo un enfoque mixto para cancelar una tarea.
Vea un ejemplo a continuación:
fuente
Las tareas tienen soporte de primera clase para la cancelación a través de tokens de cancelación . Cree sus tareas con tokens de cancelación y cancele las tareas a través de estos explícitamente.
fuente
Puede usar a
CancellationToken
para controlar si la tarea se cancela. ¿Estás hablando de abortarlo antes de que comience ("no importa, ya hice esto") o de interrumpirlo en el medio? Si es lo primero,CancellationToken
puede ser útil; si es lo último, probablemente necesitará implementar su propio mecanismo de "rescate" y verificar en los puntos apropiados en la ejecución de la tarea si debe fallar rápidamente (aún puede usar CancellationToken para ayudarlo, pero es un poco más manual).MSDN tiene un artículo sobre la cancelación de tareas: http://msdn.microsoft.com/en-us/library/dd997396.aspx
fuente
Las tareas se ejecutan en ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que abortar el hilo no puede afectar las tareas. Para abortar tareas, vea Cancelación de tareas en msdn.
fuente
Lo intenté
CancellationTokenSource
pero no puedo hacer esto. Y lo hice a mi manera. Y funciona.Y otra clase de llamar al método:
fuente
Puede abortar una tarea como un hilo si puede hacer que la tarea se cree en su propio hilo y llamar
Abort
a suThread
objeto. De forma predeterminada, una tarea se ejecuta en un subproceso de grupo de subprocesos o en el subproceso de llamada, ninguno de los cuales normalmente desea anular.Para garantizar que la tarea tenga su propio hilo, cree un planificador personalizado derivado de
TaskScheduler
. En su implementación deQueueTask
, cree un nuevo hilo y úselo para ejecutar la tarea. Más tarde, puede abortar el subproceso, lo que hará que la tarea se complete en un estado defectuoso con unThreadAbortException
.Use este programador de tareas:
Comience su tarea así:
Más tarde, puedes abortar con:
Tenga en cuenta que la advertencia sobre abortar un hilo todavía se aplica:
fuente
Thread.Abort
no es compatible con .NET Core. Intentar usarlo da como resultado una excepción: System.PlatformNotSupportedException: Thread abort no es compatible con esta plataforma. Una tercera advertencia es queSingleThreadTaskScheduler
no se puede usar de manera efectiva con tareas de estilo prometedor, en otras palabras, con tareas creadas conasync
delegados. Por ejemplo, un incrustado seawait Task.Delay(1000)
ejecuta en ningún subproceso, por lo que no se ve afectado por los eventos de subproceso.