¿Cómo puedo abortar / cancelar Tareas de TPL?


En un hilo, creo un poco de System.Threading.Task y comienzo cada tarea.

Cuando hago un .Abort() para matar el hilo, las tareas no se abortan.

¿Cómo puedo transmitir el .Abort() a mis tareas ?

Author: Tim Lloyd, 2011-01-24

11 answers

No se puede. Las tareas usan subprocesos en segundo plano del grupo de subprocesos. Tampoco se recomienda cancelar hilos usando el método Abortar. Usted puede echar un vistazo a la siguiente entrada de blog 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();
    }
}
 191
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 16:11:09

Abortar una tarea es fácilmente posible si captura el hilo en el que se está ejecutando la tarea. 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();
}

Usé Tarea.Run () para mostrar el caso de uso más común para esto, usando la comodidad de las Tareas con código antiguo de subproceso único, que no usa la clase CancellationTokenSource para determinar si debe cancelarse o no.

 27
Author: Florian Rappl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-06 21:19:03

Como este 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 utilizar este enfoque. Si puede controlar el código que se ejecuta en la tarea, será mejor que vaya con el manejo adecuado de la cancelación.

 24
Author: starteleport,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 06:26:20

Este tipo de cosas es una de las razones logísticas por las que Abort está en desuso. En primer lugar, no use Thread.Abort() para cancelar o detener un hilo si es posible. Abort() sólo debería utilizarse para matar por la fuerza un hilo que no responde a peticiones más pacíficas de detenerse de manera oportuna.

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

 17
Author: Adam Robinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 15:48:23

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

Además, recomendaría cambiar su hilo principal para funcionar a través de un CancellationToken también. Llamar a Thread.Abort() es una mala idea - puede llevar a varios problemas que son muy difíciles de diagnosticar. En su lugar, ese hilo puede usar la misma Cancelación que tus tareas usan-y la misma CancellationTokenSource se puede usar para activar la cancelación de todas tus tareas y tu hilo principal.

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

 7
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 15:46:53

Para responder a la pregunta de Prerak K sobre cómo usar CancellationTokens cuando no se usa un método anónimo en Task.Fábrica.StartNew(), pasas el CancellationToken como un parámetro en el método con el que estás comenzando StartNew (), como se muestra en el ejemplo de MSDN aquí.

Por ejemplo

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, 

}
 7
Author: Jools,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 13:49:39

Utilizo un enfoque mixto para cancelar una tarea.

  • 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 portarse mal y matarlo usando un método de la vieja escuela Abortar.

Checkout 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();
        }
    }
}
 5
Author: Alex Klaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-06 04:06:37

Puede usar un CancellationToken para controlar si la tarea se cancela. ¿Estás hablando de abortarlo antes de que comience ("nevermind, ya hice esto"), o realmente interrumpirlo en el medio? Si el primero, el CancellationToken puede ser útil; si el segundo, 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 el CancellationToken para ayudarlo, pero es un poco más manual).

MSDN tiene un artículo sobre cancelar tareas: http://msdn.microsoft.com/en-us/library/dd997396.aspx

 3
Author: Hank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 15:45:30

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.

 2
Author: Tim Lloyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 15:44:54

La tarea se está ejecutando en el ThreadPool (al menos, si está utilizando la fábrica predeterminada), por lo que abortar el subproceso no puede afectar a las tareas. Para abortar tareas, consulte Cancelación de tareas en msdn.

 2
Author: Oliver Hanappi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-24 15:46:46

Lo intenté 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 del método que llama:

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();
        }
    }
}
 0
Author: Hasan Tuna Oruç,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-30 20:59:04