How do I abort/cancel TPL Tasks?

180,020

Solution 1

You can't. Tasks use background threads from the thread pool. Also canceling threads using the Abort method is not recommended. You may take a look at the following blog post which explains a proper way of canceling tasks using cancellation tokens. Here's an example:

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

Solution 2

Like this post suggests, this can be done in the following way:

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

Although it works, it's not recommended to use such approach. If you can control the code that executes in task, you'd better go with proper handling of cancellation.

Solution 3

Aborting a Task is easily possible if you capture the thread in which the task is running in. Here is an example code to demonstrate this:

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

I used Task.Run() to show the most common use-case for this - using the comfort of Tasks with old single-threaded code, which does not use the CancellationTokenSource class to determine if it should be canceled or not.

Solution 4

This sort of thing is one of the logistical reasons why Abort is deprecated. First and foremost, do not use Thread.Abort() to cancel or stop a thread if at all possible. Abort() should only be used to forcefully kill a thread that is not responding to more peaceful requests to stop in a timely fashion.

That being said, you need to provide a shared cancellation indicator that one thread sets and waits while the other thread periodically checks and gracefully exits. .NET 4 includes a structure designed specifically for this purpose, the CancellationToken.

Solution 5

I use a mixed approach to cancel a task.

  • Firstly, I'm trying to Cancel it politely with using the Cancellation.
  • If it's still running (e.g. due to a developer's mistake), then misbehave and kill it using an old-school Abort method.

Checkout an example below:

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();
        }
    }
}
Share:
180,020

Related videos on Youtube

Patrice Pezillier
Author by

Patrice Pezillier

Updated on August 30, 2021

Comments

  • Patrice Pezillier
    Patrice Pezillier over 2 years

    In a thread, I create some System.Threading.Task and start each task.

    When I do a .Abort() to kill the thread, the tasks are not aborted.

    How can I transmit the .Abort() to my tasks ?

  • Prerak K
    Prerak K over 10 years
    Nice explanation. I have a question, how does it work when we don't have an anonymous method in the Task.Factory.StartNew ? like Task.Factory.StartNew(() => ProcessMyMethod(),cancellationToken)
  • Christoph Fink
    Christoph Fink almost 10 years
    Thanks for this idea. Used this approach to implement a timeout for some external code, which has no CancellationToken support...
  • mehmet6parmak
    mehmet6parmak almost 10 years
    what if there is a blocking call which does not return inside the executing task?
  • Joel
    Joel almost 10 years
    +1 for giving a different approach while stating its fallbacks. I didn't know this could be done :)
  • Martin Meeser
    Martin Meeser over 9 years
    AFAIK thread.abort will leave you unknown about your stack, it might be invalid. I have never tried it but I guess starting a thread in seperate app domain that thread.abort will be save! Besides, a whole thread is wasted in your solution just to abort one task. You would not have to use tasks but threads in first place. (downvote)
  • Florian Rappl
    Florian Rappl over 9 years
    As I wrote - this solution is a last resort that might be considered under certain circumstances. Of course a CancellationToken or even simpler solutions that are race-condition free should be considered. The code above does only illustrate the method, not the area of usage.
  • Mark
    Mark over 9 years
    @mehmet6parmak I think the only thing you can do then is use Task.Wait(TimeSpan / int) to give it a (time-based) deadline from the outside.
  • Cheshire Cat
    Cheshire Cat over 9 years
    What if I have my custom class to manage the execution of methods inside a new Task? Something like: public int StartNewTask(Action method). Inside the StartNewTask method I create a new Task by: Task task = new Task(() => method()); task.Start();. So how can I manage the CancellationToken? I would also like to know if like Thread I need to implement a logic to check if there're some Tasks that are still hanging and so kill them when Form.Closing. With Threads I use Thread.Abort().
  • Hi-Angel
    Hi-Angel over 9 years
    Omg, what a bad example! It is a simple boolean condition, of course it is the first what one would try! But i.e. I have a function in a separate Task, which may take much time to be ended, and ideally it shouldn't know anything about a threading or whatever. So how do I cancel the function with your advice?
  • Ivaylo Slavov
    Ivaylo Slavov over 8 years
    I think this approach might have unknown consequences and I would not recommend it in production code. Tasks are not always guaranteed to be executed in a different thread, meaning they can be ran in the same thread as the one that created them if the scheduler decides so (which will mean the main thread will be passed to the thread local variable). In your code you might then end up aborting the main thread, which is not what you really want. Perhaps a check if the threads are the same before the abort would be a good think to have, if you insist on aborting
  • Sam
    Sam over 7 years
    Thanks for solution! We can just pass token to method and cancel tokensource, instead of somehow getting thread instance from method and aborting this instance directly.
  • Gordon Bean
    Gordon Bean over 7 years
    @Martin - As I have researched this question on SO, I see you have down-voted several answers that use Thread.Abort to kill tasks, but you have not provided any alternative solutions. How can you kill 3rd party code that does not support cancellation and is running in a Task?
  • tekHedd
    tekHedd over 7 years
    The only solution I can find is to use my own thread pool, and if it does become necessary to abort a stuck thread, replace it manually. It's just not practical to use Tasks where you might have to use Abort, which means that third party code and async also don't mix, as far as I'm concerned.
  • user2288650
    user2288650 over 6 years
    Does this method work for blocking functions. I am trying to reset a blocking function but this does not seem to help. The blocking function linger after calling- CancellationTokenSource.cancel();
  • John Henckel
    John Henckel almost 5 years
    @GordonBean Here is a post that gives a nice general purpose way to abort threads safely. stackoverflow.com/a/55709530/1812732
  • Theodor Zoulias
    Theodor Zoulias about 4 years
    This code fails with a runtime exception: System.InvalidOperationException: RunSynchronously may not be called on a task that was already started.
  • Edward Brey
    Edward Brey about 4 years
    @TheodorZoulias Good catch. Thanks. I fixed the code and generally improved the answer.
  • Theodor Zoulias
    Theodor Zoulias about 4 years
    Yeap, this fixed the bug. Another caveat that it should be probably mentioned is that Thread.Abort is not supported on .NET Core. Attempting to use it there results to an exception: System.PlatformNotSupportedException: Thread abort is not supported on this platform. A third caveat is that the SingleThreadTaskScheduler can not be used effectively with promise-style tasks, in other words with tasks created with async delegates. For example an embedded await Task.Delay(1000) runs in no thread, so it is unaffected be thread events.
  • Edward Brey
    Edward Brey about 4 years
    "You can't. Tasks use background threads from the thread pool." That's not necessarily true. You can create a dedicated thread for your task and then abort that thread.
  • Edward Brey
    Edward Brey about 4 years
    The task's thread that gets aborted may be a thread-pool thread or the thread of the caller invoking the task. To avoid this, you can use a TaskScheduler to specify a dedicated thread for the task.
  • Toolkit
    Toolkit over 3 years
    @Mark you are wrong, Task.Wait will just abandon the task
  • Toolkit
    Toolkit over 3 years
    'SingleThreadTaskScheduler' does not implement inherited abstract member 'TaskScheduler.TryExecuteTaskInline(Task, bool)
  • Edward Brey
    Edward Brey over 3 years
    @Toolkit Good catch. The last method had the wrong name. It was supposed to be TryExecuteTaskInline. I updated the code snippet.
  • Toolkit
    Toolkit over 3 years
    @EdwardBrey thank you, still Abort() doesn't really abort anything, it works as CancellationToken, can't break long operation
  • Edward Brey
    Edward Brey over 3 years
    @Toolkit Abort() here is Thread.Abort, which "Raises a ThreadAbortException in the thread on which it is invoked". It does so immediately, even amid a long operation. That's why you see all the caveats in the remarks. It is unrelated to CancellationToken.
  • Latency
    Latency almost 3 years
    This is no longer supported in .NET 5.0+ You will want to look at alternatives to this.
  • Latency
    Latency almost 3 years
    This is no longer supported in .NET Framework versions as of 5.0+