77

We could abort a Thread like this:

Thread thread = new Thread(SomeMethod);
.
.
.
thread.Abort();

But can I abort a Task (in .Net 4.0) in the same way not by cancellation mechanism. I want to kill the Task immediately.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Amir Karimi
  • 5,401
  • 4
  • 32
  • 51
  • 1
    Extra reading from the MSDN: "[Cancellation in Managed Threads](https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx)" – Scott Chamberlain Oct 17 '16 at 14:58
  • Somewhat related: [What's wrong with using Thread.Abort?](https://stackoverflow.com/questions/1559255/whats-wrong-with-using-thread-abort) – Theodor Zoulias Aug 02 '21 at 12:35

9 Answers9

53

The guidance on not using a thread abort is controversial. I think there is still a place for it but in exceptional circumstance. However you should always attempt to design around it and see it as a last resort.

Example;

You have a simple windows form application that connects to a blocking synchronous web service. Within which it executes a function on the web service within a Parallel loop.

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    Thread.Sleep(120000); // pretend web service call

});

Say in this example, the blocking call takes 2 mins to complete. Now I set my MaxDegreeOfParallelism to say ProcessorCount. iListOfItems has 1000 items within it to process.

The user clicks the process button and the loop commences, we have 'up-to' 20 threads executing against 1000 items in the iListOfItems collection. Each iteration executes on its own thread. Each thread will utilise a foreground thread when created by Parallel.ForEach. This means regardless of the main application shutdown, the app domain will be kept alive until all threads have finished.

However the user needs to close the application for some reason, say they close the form. These 20 threads will continue to execute until all 1000 items are processed. This is not ideal in this scenario, as the application will not exit as the user expects and will continue to run behind the scenes, as can be seen by taking a look in task manger.

Say the user tries to rebuild the app again (VS 2010), it reports the exe is locked, then they would have to go into task manager to kill it or just wait until all 1000 items are processed.

I would not blame you for saying, but of course! I should be cancelling these threads using the CancellationTokenSource object and calling Cancel ... but there are some problems with this as of .net 4.0. Firstly this is still never going to result in a thread abort which would offer up an abort exception followed by thread termination, so the app domain will instead need to wait for the threads to finish normally, and this means waiting for the last blocking call, which would be the very last running iteration (thread) that ultimately gets to call po.CancellationToken.ThrowIfCancellationRequested. In the example this would mean the app domain could still stay alive for up to 2 mins, even though the form has been closed and cancel called.

Note that Calling Cancel on CancellationTokenSource does not throw an exception on the processing thread(s), which would indeed act to interrupt the blocking call similar to a thread abort and stop the execution. An exception is cached ready for when all the other threads (concurrent iterations) eventually finish and return, the exception is thrown in the initiating thread (where the loop is declared).

I chose not to use the Cancel option on a CancellationTokenSource object. This is wasteful and arguably violates the well known anti-patten of controlling the flow of the code by Exceptions.

Instead, it is arguably 'better' to implement a simple thread safe property i.e. Bool stopExecuting. Then within the loop, check the value of stopExecuting and if the value is set to true by the external influence, we can take an alternate path to close down gracefully. Since we should not call cancel, this precludes checking CancellationTokenSource.IsCancellationRequested which would otherwise be another option.

Something like the following if condition would be appropriate within the loop;

if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional || stopExecuting) {loopState.Stop(); return;}

The iteration will now exit in a 'controlled' manner as well as terminating further iterations, but as I said, this does little for our issue of having to wait on the long running and blocking call(s) that are made within each iteration (parallel loop thread), since these have to complete before each thread can get to the option of checking if it should stop.

In summary, as the user closes the form, the 20 threads will be signaled to stop via stopExecuting, but they will only stop when they have finished executing their long running function call.

We can't do anything about the fact that the application domain will always stay alive and only be released when all foreground threads have completed. And this means there will be a delay associated with waiting for any blocking calls made within the loop to complete.

Only a true thread abort can interrupt the blocking call, and you must mitigate leaving the system in a unstable/undefined state the best you can in the aborted thread's exception handler which goes without question. Whether that's appropriate is a matter for the programmer to decide, based on what resource handles they chose to maintain and how easy it is to close them in a thread's finally block. You could register with a token to terminate on cancel as a semi workaround i.e.

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Parallel.ForEach(iListOfItems, po, (item, loopState) =>
{

    using (cts.Token.Register(Thread.CurrentThread.Abort))
    {
        Try
        {
           Thread.Sleep(120000); // pretend web service call          
        }
        Catch(ThreadAbortException ex)
        {
           // log etc.
        }
        Finally
        {
          // clean up here
        }
    }

});

but this will still result in an exception in the declaring thread.

All things considered, interrupt blocking calls using the parallel.loop constructs could have been a method on the options, avoiding the use of more obscure parts of the library. But why there is no option to cancel and avoid throwing an exception in the declaring method strikes me as a possible oversight.

Microsoft Developer
  • 1,919
  • 1
  • 20
  • 27
  • 2
    Hi Terry, I tried your example of using the thread abort, and it doesn't work as expected. If you think about it, your long running operation (in this case the thread.sleep) is making the worker thread hang, therefore the cancellation callback can't run on that thread, and therefore Thread.CurrentThread.Abort cannot be called on the thread that has hanged. You can observe this by converting your callback into a method group and expecting the thread id in the debugger. In my case, the abort gets called on the thread that called Cancel on the token source. – Doctor Jones Nov 08 '17 at 14:06
  • 2
    This is a great solution until the advant of NET 5.0 and Core. You will not be able to use Thread.Abort of any kind as it is PlatformNotSupportedException. – Latency Aug 31 '20 at 15:45
  • Indeed, Just to note, things have moved on quite a bit since then and new guidance has been issued against the newer framework. See Remarks: https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.abort?view=net-5.0 – Microsoft Developer Sep 01 '20 at 16:23
45

But can I abort a Task (in .Net 4.0) in the same way not by cancellation mechanism. I want to kill the Task immediately.

Other answerers have told you not to do it. But yes, you can do it. You can supply Thread.Abort() as the delegate to be called by the Task's cancellation mechanism. Here is how you could configure this:

class HardAborter
{
  public bool WasAborted { get; private set; }
  private CancellationTokenSource Canceller { get; set; }
  private Task<object> Worker { get; set; }

  public void Start(Func<object> DoFunc)
  {
    WasAborted = false;

    // start a task with a means to do a hard abort (unsafe!)
    Canceller = new CancellationTokenSource();

    Worker = Task.Factory.StartNew(() => 
      {
        try
        {
          // specify this thread's Abort() as the cancel delegate
          using (Canceller.Token.Register(Thread.CurrentThread.Abort))
          {
            return DoFunc();
          }
        }
        catch (ThreadAbortException)
        {
          WasAborted = true;
          return false;
        }
      }, Canceller.Token);
  }

  public void Abort()
  {
    Canceller.Cancel();
  }

}

disclaimer: don't do this.

Here is an example of what not to do:

 var doNotDoThis = new HardAborter();

 // start a thread writing to the console
 doNotDoThis.Start(() =>
    {
       while (true)
       {
          Thread.Sleep(100);
          Console.Write(".");
       }
       return null;
    });


 // wait a second to see some output and show the WasAborted value as false
 Thread.Sleep(1000);
 Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted);

 // wait another second, abort, and print the time
 Thread.Sleep(1000);
 doNotDoThis.Abort();
 Console.WriteLine("Abort triggered at " + DateTime.Now);

 // wait until the abort finishes and print the time
 while (!doNotDoThis.WasAborted) { Thread.CurrentThread.Join(0); }
 Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted + " at " + DateTime.Now);

 Console.ReadKey();

output from sample code

jltrem
  • 12,124
  • 4
  • 40
  • 50
  • 11
    As I do understand the Tasks you should not mix Task and Thread functionality. One thread could host many tasks, you do abort a whole thread (with all its negative impacts) just to abort one task. I really have to down-vote on this. – Martin Meeser Oct 13 '14 at 08:51
  • 3
    This is a great solution, but unfortunately, Thread.CurrentThread.Abort is not supported on .NET.Core. Does anyone has a solution for this? – Tomas Kubes Jan 23 '20 at 13:10
  • 2
    Not supported in .NET 5.0 + or Core. Application Domain container trick isn't supported either. I agree.. there are instances where middle-tier layers will block indefinatly and unable to re-write lower layer. – Latency Aug 31 '20 at 15:46
41
  1. You shouldn't use Thread.Abort()
  2. Tasks can be Cancelled but not aborted.

The Thread.Abort() method is (severely) deprecated.

Both Threads and Tasks should cooperate when being stopped, otherwise you run the risk of leaving the system in a unstable/undefined state.

If you do need to run a Process and kill it from the outside, the only safe option is to run it in a separate AppDomain.


This answer is about .net 3.5 and earlier.

Thread-abort handling has been improved since then, a.o. by changing the way finally blocks work.

But Thread.Abort is still a suspect solution that you should always try to avoid.


And in .net Core (.net 5+) Thread.Abort() will now throw a PlatformNotSupportedException .

Kind of underscoring the 'deprecated' point.

H H
  • 263,252
  • 30
  • 330
  • 514
  • So, is there any option to inoke a method by reflection which I can cancel it? (I dont know about content of the invoked method) – Amir Karimi Dec 05 '10 at 17:02
  • 3
    @amkh - The Task library includes a cancelation mechanism called a CancelationToken. Why don't you use that? – Erik Funkenbusch Dec 05 '10 at 17:09
  • I try cancellation scenario but there is no success! Let me say what I want to do: I wrote an application that simply has a textbox to write some C# code snippets. It generates an assembly from the given code and then invokes the EntryPoint method. I try now using the AppDomain but for now I could not control the generated assembly console output. When I used reflection I redirected the console to my own stream successfuly. By the way, thanks a lot for AppDomain approach. I will change my design to using AppDomain but I've been curious. – Amir Karimi Dec 05 '10 at 17:27
  • 2
    @amkh: AppDomain looks the right way for you. You don't want all that unpredictable code to run in your main memoryspace. – H H Dec 05 '10 at 17:39
  • "otherwise you run the risk of " .. Since when problems starts from the point where you obtain the risk? Use ThreadAbortException and control resources of background thread in main thread. – Roman Pokrovskij Feb 10 '13 at 14:38
  • 78
    If you invoke a third-party component which blocks indefinitely (which, of course, it shouldn't do, but which we're encountering here), you cannot include the code necessary to poll your cancellation token inside that component. And you shouldn't have to create a separate executable and AppDomain for any code or component which might fail to return (or not return in a timely fashion) from a blocking call! – ALEXintlsos May 17 '13 at 17:39
  • 46
    "... (severely) deprecated." [citation needed] – Qwertie Feb 05 '14 at 23:09
  • 1
    @Qwertie - that refers to the [Fx 3.5 and before documentation](http://msdn.microsoft.com/en-us/library/system.threading.thread.abort%28v=vs.85%29.aspx). A little hard to find now, it has been cleaned up. But note the post at the bottom. Handling of AbortException has been largely fixed now (finally blocks) but it's still not a totally reliable approach. – H H Feb 05 '14 at 23:20
  • You can catch a thread abort and call Thread.ResetAbort() to stop it. It's a convenient way to cause a stack to quickly unwind triggered from a watchdog thread. Been doing this for years in production with hundreds of threads running simultaneously. Never had an issue, never had a hung thread. Though, doing this under a Task may not be desirable, because the thread can change, or be back in the thread pool during an await. – Brain2000 Apr 22 '20 at 20:23
  • Reminder that not everything in the world is a WebApp or GUI. I've got a nonconvex nonlinear programming problem which calls an ODE integrator as part of each function evaluation. It is quite literally all "just math" that takes a long time, that I want to run async. But I cannot ever prove that is has been sufficiently debugged so it will never hang. The solvers are out of my control. The ODE kernel could have code in it to handle termination -- but that is a gross violation of separation of concerns, the kernel shouldn't even know it is running in a thread. – lamont May 14 '21 at 21:45
21

Everyone knows (hopefully) its bad to terminate thread. The problem is when you don't own a piece of code you're calling. If this code is running in some do/while infinite loop , itself calling some native functions, etc. you're basically stuck. When this happens in your own code termination, stop or Dispose call, it's kinda ok to start shooting the bad guys (so you don't become a bad guy yourself).

So, for what it's worth, I've written those two blocking functions that use their own native thread, not a thread from the pool or some thread created by the CLR. They will stop the thread if a timeout occurs:

// returns true if the call went to completion successfully, false otherwise
public static bool RunWithAbort(this Action action, int milliseconds) => RunWithAbort(action, new TimeSpan(0, 0, 0, 0, milliseconds));
public static bool RunWithAbort(this Action action, TimeSpan delay)
{
    if (action == null)
        throw new ArgumentNullException(nameof(action));

    var source = new CancellationTokenSource(delay);
    var success = false;
    var handle = IntPtr.Zero;
    var fn = new Action(() =>
    {
        using (source.Token.Register(() => TerminateThread(handle, 0)))
        {
            action();
            success = true;
        }
    });

    handle = CreateThread(IntPtr.Zero, IntPtr.Zero, fn, IntPtr.Zero, 0, out var id);
    WaitForSingleObject(handle, 100 + (int)delay.TotalMilliseconds);
    CloseHandle(handle);
    return success;
}

// returns what's the function should return if the call went to completion successfully, default(T) otherwise
public static T RunWithAbort<T>(this Func<T> func, int milliseconds) => RunWithAbort(func, new TimeSpan(0, 0, 0, 0, milliseconds));
public static T RunWithAbort<T>(this Func<T> func, TimeSpan delay)
{
    if (func == null)
        throw new ArgumentNullException(nameof(func));

    var source = new CancellationTokenSource(delay);
    var item = default(T);
    var handle = IntPtr.Zero;
    var fn = new Action(() =>
    {
        using (source.Token.Register(() => TerminateThread(handle, 0)))
        {
            item = func();
        }
    });

    handle = CreateThread(IntPtr.Zero, IntPtr.Zero, fn, IntPtr.Zero, 0, out var id);
    WaitForSingleObject(handle, 100 + (int)delay.TotalMilliseconds);
    CloseHandle(handle);
    return item;
}

[DllImport("kernel32")]
private static extern bool TerminateThread(IntPtr hThread, int dwExitCode);

[DllImport("kernel32")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, IntPtr dwStackSize, Delegate lpStartAddress, IntPtr lpParameter, int dwCreationFlags, out int lpThreadId);

[DllImport("kernel32")]
private static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32")]
private static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • 2
    I like this answer!. As one smart guy says "every problem in software can be solved by adding a level of indirection" – John Henckel Jun 27 '19 at 16:23
  • 1
    I appreciate the "hopefully" part of this answer. Unfortunately I find that as their are always new developers this is not a given. – Max Young Jul 14 '19 at 13:45
  • Does it matter whether you call the Win32 `CreateThread` directly versus creating a .NET `Thread` object and calling `Start`? – Edward Brey Feb 26 '20 at 15:37
  • @EdwardBrey - yes, I don't remember exactly but it didn't work at some point or in certain circumstances using a standard .NET thread. – Simon Mourier Feb 26 '20 at 17:27
  • 1
    Quick [search result](https://learn.microsoft.com/en-us/dotnet/standard/threading/managed-and-unmanaged-threading-in-windows): "TerminateThread does not execute finally clauses or free up resources, and cannot be prevented. However, Thread.Abort executes all your rollback code, reclaims all the resources, and can be denied using ResetAbort." It seems that managed threads is what you'd want for RunWithAbort. I wonder why it didn't work. – Edward Brey Feb 26 '20 at 21:11
  • @EdwardBrey - My code is typically not for .NET threads, it's for any kind of thread, so managed thread is certainly not what I want and I remember clearly it does not work for all scenarios. – Simon Mourier Feb 26 '20 at 22:19
  • This still causes CLR exceptions for me – Tomer Mar 28 '20 at 15:18
  • @Tomer - I never said anything about exceptions, the only purpose is to kill threads, it's very possible that exceptions be raised. – Simon Mourier Mar 28 '20 at 15:57
  • 1
    This works perfectly. ThreadAbortException is correctly raised on the aborted thread and all its sub-threads. All Catches and Finally blocks are run. I found no apparent side-effects and the application continues to run. – smirkingman Oct 01 '20 at 13:30
2

While it's possible to abort a thread, in practice it's almost always a very bad idea to do so. Aborthing a thread means the thread is not given a chance to clean up after itself, leaving resources undeleted, and things in unknown states.

In practice, if you abort a thread, you should only do so in conjunction with killing the process. Sadly, all too many people think ThreadAbort is a viable way of stopping something and continuing on, it's not.

Since Tasks run as threads, you can call ThreadAbort on them, but as with generic threads you almost never want to do this, except as a last resort.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • 12
    "not given a chance to clean up after itself, leaving resources undeleted, and things in unknown states. " ... not exactly. Thread.Abort raises a ThreadAbortException on the thread within which you should perform any clean up procedures. – Microsoft Developer Jan 17 '11 at 11:44
  • 3
    Calling threadabort on a threadpool managed thread is only ever going to end in tears... Calling threadabort on your own thread, providing you handle ThreadAbortException is fine. – Immortal Blue Dec 28 '13 at 14:36
  • @ImmortalBlue, i agree, and a good example is Simon's post https://stackoverflow.com/a/55709530/1812732 – John Henckel Jun 27 '19 at 16:25
0

I faced a similar problem with Excel's Application.Workbooks.

If the application is busy, the method hangs eternally. My approach was simply to try to get it in a task and wait, if it takes too long, I just leave the task be and go away (there is no harm "in this case", Excel will unfreeze the moment the user finishes whatever is busy).

In this case, it's impossible to use a cancellation token. The advantage is that I don't need excessive code, aborting threads, etc.

public static List<Workbook> GetAllOpenWorkbooks()
{
    //gets all open Excel applications
    List<Application> applications = GetAllOpenApplications();

    //this is what we want to get from the third party library that may freeze
    List<Workbook> books = null;

    //as Excel may freeze here due to being busy, we try to get the workbooks asynchronously
    Task task = Task.Run(() =>
    {
        try 
        { 
            books = applications
                .SelectMany(app => app.Workbooks.OfType<Workbook>()).ToList();
        }
        catch { }
    });
    
    //wait for task completion
    task.Wait(5000);
    return books; //handle outside if books is null
}
Daniel Möller
  • 84,878
  • 18
  • 192
  • 214
  • The [`Task.Wait`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.wait) method takes an `int millisecondsTimeout` argument. It might be simpler than what you are doing. – Theodor Zoulias Apr 19 '22 at 15:14
  • 1
    Weirdly, when I use `Task.Wait`, excel throws an exception saying it's busy. That didn't happen before.... Nevertheless, thank you for the tip, updated the answer. – Daniel Möller Apr 19 '22 at 16:23
0

This is my implementation of an idea presented by @Simon-Mourier, using the dotnet thread, short and simple code:

    public static bool RunWithAbort(this Action action, int milliseconds)
    {
         if (action == null) throw new ArgumentNullException(nameof(action));

        var success = false;
        var thread = new Thread(() =>
        {
            action();
            success = true;
        });
        thread.IsBackground = true;
        thread.Start();
        thread.Join(milliseconds);          
        thread.Abort();

        return success;
    }
codeDom
  • 1,623
  • 18
  • 54
-1

You can "abort" a task by running it on a thread you control and aborting that thread. This causes the task to complete in a faulted state with a ThreadAbortException. You can control thread creation with a custom task scheduler, as described in this answer. Note that the caveat about aborting a thread applies.

(If you don't ensure the task is created on its own thread, aborting it would abort either a thread-pool thread or the thread initiating the task, neither of which you typically want to do.)

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
  • The `Task` is not aborted. It is completed in a faulted state, its [`Exception`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.exception) being *System.Threading.ThreadAbortException: Thread was being aborted.* Threads and tasks have different semantics. – Theodor Zoulias Mar 30 '20 at 16:18
  • 1
    @TheodorZoulias That a good distinction. To help clarify, I put quotes around the word "abort" in the answer and incorporated your detail about the task's completion state. I also linked to the caveat about aborting a thread, to help avoid unpleasant surprises. – Edward Brey Mar 31 '20 at 00:30
-2
using System;
using System.Threading;
using System.Threading.Tasks;

...

var cts = new CancellationTokenSource();
var task = Task.Run(() => { while (true) { } });
Parallel.Invoke(() =>
{
    task.Wait(cts.Token);
}, () =>
{
    Thread.Sleep(1000);
    cts.Cancel();
});

This is a simple snippet to abort a never-ending task with CancellationTokenSource.

  • 5
    This does not abort the task. This just stops waiting for the task to finish. It actually keeps on running while the caller of `Task.Wait(...)` will resume with the next statement. You can easily test this by setting a break point inside the `while` loop. – Silvermind Jul 18 '17 at 22:51