2011-01-24 12 views

Respuesta

176

No puede. Las tareas usan subprocesos de fondo del grupo de subprocesos. Tampoco se recomienda cancelar subprocesos utilizando el método Abortar. Puede echar un vistazo al following blog post que explica una forma adecuada de cancelar tareas utilizando tokens de cancelación. He aquí un ejemplo:

class Program 
{ 
    static void Main() 
    { 
     var ts = new CancellationTokenSource(); 
     CancellationToken ct = ts.Token; 
     Task.Factory.StartNew(() => 
     { 
      while (true) 
      { 
       // do some heavy work here 
       Thread.Sleep(100); 
       if (ct.IsCancellationRequested) 
       { 
        // another thread decided to cancel 
        Console.WriteLine("task canceled"); 
        break; 
       } 
      } 
     }, ct); 

     // Simulate waiting 3s for the task to complete 
     Thread.Sleep(3000); 

     // Can't wait anymore => cancel this task 
     ts.Cancel(); 
     Console.ReadLine(); 
    } 
} 
+4

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) –

+1

¿Por qué no? Aquí hay un [ejemplo] (http://stackoverflow.com/a/19311606/1845402). – starteleport

+39

¿Qué sucede si hay una llamada de bloqueo que no retorna dentro de la tarea de ejecución? – mehmet6parmak

2

La tarea se está ejecutando en el ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que cancelar el hilo no puede afectar las tareas. Para abortar tareas, vea Task Cancellation en msdn.

2

Las tareas tienen soporte de primera clase para la cancelación a través de cancellation tokens. Cree sus tareas con tokens de cancelación y cancele las tareas a través de estos explícitamente.

3

Puede usar un CancellationToken para controlar si la tarea se cancela. ¿Estás hablando de abortar antes de que se inicie ("no importa, ya lo hice"), o interrumpirlo en el medio? Si el primero, el CancellationToken puede ser útil; en este último caso, es probable que deba 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 utilizar el CancelToken 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

7

Usted no debe tratar de hacerlo directamente. Diseñe sus tareas para que funcionen con un CancellationToken, y cancélelas de esta manera.

Además, recomendaría cambiar el hilo principal para que funcione a través de un CancellationToken también. Llamar al Thread.Abort() es una mala idea, ya que puede dar lugar a varios problemas que son muy difíciles de diagnosticar. En su lugar, ese hilo puede usar el mismo Cancellation que usan sus tareas, y el mismo CancellationTokenSource se puede usar para desencadenar la cancelación de todas las de sus tareas y su hilo principal.

Esto conducirá a un diseño mucho más simple y seguro.

15

Este tipo de cosas es una de las razones logísticas por las que Abort está en desuso. Antes que nada, 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 detenerlo de manera oportuna.

Dicho esto, debe proporcionar un indicador de cancelación compartido que establece un hilo y espera mientras el otro hilo revisa periódicamente y sale con gracia. .NET 4 incluye una estructura diseñada específicamente para este propósito, el CancellationToken.

22

Abortar una tarea es posible fácilmente si captura el hilo en el que la tarea se ejecuta en Aquí hay un código de ejemplo para demostrar esto:.

void Main() 
{ 
    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 displayed 
     Console.WriteLine("Task finished!"); 
    }); 

    //This is needed in the example to avoid thread being still NULL 
    Thread.Sleep(10); 

    //Cancel the task by aborting the thread 
    thread.Abort(); 
} 

Solía ​​tareas.Ejecute() para mostrar el caso de uso más común para esto, utilizando la comodidad de Tareas con el código de un solo subproceso antiguo, que no utiliza la clase CancellationTokenSource para determinar si debe cancelarse o no.

+2

Gracias por esta idea. Usó este enfoque para implementar un tiempo de espera para un código externo, que no tiene soporte de 'CancellationToken' ... – ChrFin

+4

AFAIK thread.abort le dejará desconocido sobre su pila, podría ser inválido.Nunca lo intenté pero supongo que comenzar un hilo en un dominio de aplicación separado que thread.abort se guardará. Además, se desperdicia todo un hilo en tu solución solo para abortar una tarea. No tendría que usar tareas sino hilos en primer lugar. (Votación abajo) –

+0

Como escribí, esta solución es un último recurso que podría considerarse bajo ciertas circunstancias. Por supuesto, se debe considerar un 'CancellationToken' o incluso soluciones 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. –

19

Como this post sugiere, esto se puede hacer de la siguiente manera:

int Foo(CancellationToken token) 
{ 
    Thread t = Thread.CurrentThread; 
    using (token.Register(t.Abort)) 
    { 
     // compute-bound work here 
    } 
} 

Aunque funciona, no se recomienda el uso de este enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que proceda con el manejo adecuado de la cancelación.

+5

+1 por dar un enfoque diferente al establecer sus retrocesos. No sabía que esto podría hacerse :) – Joel

+0

¡Gracias por la solución! Solo podemos pasar el token al método y cancelar tokensource, en lugar de obtener de algún modo la instancia del hilo del método y cancelar esta instancia directamente. – Sam

4

Uso un enfoque mixto para cancelar una tarea.

  • En primer lugar, estoy tratando de cancelarlo educadamente con el uso del Cancellation.
  • Si aún se está ejecutando (por ejemplo, debido a un error del desarrollador), entonces se porte mal y mátalo utilizando un método Abort de la vieja escuela.

Pedido un ejemplo a continuación:

private CancellationTokenSource taskToken; 
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false); 

void Main() 
{ 
    // Start a task which is doing nothing but sleeps 1s 
    LaunchTaskAsync(); 
    Thread.Sleep(100); 
    // Stop the task 
    StopTask(); 
} 

/// <summary> 
///  Launch task in a new thread 
/// </summary> 
void LaunchTaskAsync() 
{ 
    taskToken = new CancellationTokenSource(); 
    Task.Factory.StartNew(() => 
     { 
      try 
      { //Capture the thread 
       runningTaskThread = Thread.CurrentThread; 
       // Run the task 
       if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000)) 
        return; 
       Console.WriteLine("Task finished!"); 
      } 
      catch (Exception exc) 
      { 
       // Handle exception 
      } 
     }, taskToken.Token); 
} 

/// <summary> 
///  Stop running task 
/// </summary> 
void StopTask() 
{ 
    // Attempt to cancel the task politely 
    if (taskToken != null) 
    { 
     if (taskToken.IsCancellationRequested) 
      return; 
     else 
      taskToken.Cancel(); 
    } 

    // Notify a waiting thread that an event has occurred 
    if (awaitReplyOnRequestEvent != null) 
     awaitReplyOnRequestEvent.Set(); 

    // If 1 sec later the task is still running, kill it cruelly 
    if (runningTaskThread != null) 
    { 
     try 
     { 
      runningTaskThread.Join(TimeSpan.FromSeconds(1)); 
     } 
     catch (Exception ex) 
     { 
      runningTaskThread.Abort(); 
     } 
    } 
} 
7

Para responder a la pregunta de prerak K sobre cómo utilizar CancellationTokens cuando no se utiliza un método anónimo en Task.Factory.StartNew(), se pasa el CancellationToken como un parámetro en el método que está comenzando con StartNew(), como se muestra en el ejemplo de MSDN here.

p. Ej.

var tokenSource = new CancellationTokenSource(); 
var token = tokenSource.Token; 

Task.Factory.StartNew(() => DoSomeWork(1, token), token); 

static void DoSomeWork(int taskNum, CancellationToken ct) 
{ 
    // Do work here, checking and acting on ct.IsCancellationRequested where applicable, 

} 
0

Probé CancellationTokenSource pero no puedo hacer esto. Y lo hice a mi manera. Y funciona.

namespace Blokick.Provider 
{ 
    public class SignalRConnectProvider 
    { 
     public SignalRConnectProvider() 
     { 
     } 

     public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`. 

     public async Task<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 la llamada al método:

namespace Blokick.Views 
{ 
    [XamlCompilation(XamlCompilationOptions.Compile)] 
    public partial class MessagePerson : ContentPage 
    { 
     SignalRConnectProvider signalR = new SignalRConnectProvider(); 

     public MessagePerson() 
     { 
      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(); 
     } 
    } 
}