Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dotnet core equivalent to Thread.Abort

Tags:

Background

I have a Service abstraction. Each service has it own WorkItem. WorkItem able to start with some data. The service is limiting the excution time of WorkItem. Let's say that a single workitem can takes up to 60 seconds. After this, the Service should kill it.

This code migrated from the .NET Framework, I created a Thread object which run the Start(model) method. Then the code was something like:

Thread t = new Thread(workItem.Start, model); t.start(); if (!t.Join(TimeSpan.FromSeconds(60)))     t.Abort(); 

The Thread.Abort was injecting an exception for the running thread, which lead it for immediately stop.

Now, I moved the code to dotnet core - as you may know, when you calling Thread.Abort() your getting the following message:

System.PlatformNotSupportedException: Thread abort is not supported on this platform.    at System.Threading.Thread.Abort()    at ... 

The Goal

I want to limit the execution time of the WorkItem to specific amount of time. Note that this limitation should work also if you running code line like this:

Thread.Sleep(61000); // 61 seconds. should be stop after 60 seconds. 

Progress

On the dotnet core world, it's seems like it's going to the Task related solution. So, I thought to use CancellationToken. But its seems like its impossible to watch the "Canceled" event and stop immediately. The examples I saw are using while (!canceled) loops, which cant stop long operations (like Thread.Sleep(1000000).

Question

How to do it right?

Update

I written this sample code:

public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock) {     try     {         Task task = Task.Factory.StartNew(() => codeBlock());         if (!task.Wait(timeSpan))         {             // ABORT HERE!             Console.WriteLine("Time exceeded. Aborted!");         }         return task.IsCompleted;     }     catch (AggregateException ae)     {         throw ae.InnerExceptions[0];     } } 

And this Main file:

public static void Main(string[] args) {     bool Completed = ExecuteWithTimeLimit(TimeSpan.FromMilliseconds(2000), () =>     {         Console.WriteLine("start");         Thread.Sleep(3000);         Console.WriteLine("end");     });      Console.WriteLine($"Completed={Completed}");     Console.ReadLine(); } 

Expected: "end" wont be printed to the screen. Actual: "end" printed. Is there any alternative that can kill a Task?

like image 967
No1Lives4Ever Avatar asked Nov 25 '18 07:11

No1Lives4Ever


People also ask

Why is thread Abort obsolete?

Abort to abort a thread other than the current thread, you don't know what code has executed or failed to execute when the ThreadAbortException is thrown. You also cannot be certain of the state of your application or any application and user state that it's responsible for preserving. For example, calling Thread.

What is thread Abort in C#?

The Abort() method is used for destroying threads. The runtime aborts the thread by throwing a ThreadAbortException. This exception cannot be caught, the control is sent to the finally block if any. Use the Abort() method on a thread − childThread.Abort();

What is the difference between thread interrupt and thread Abort?

Abort method throws a ThreadAbortException, the Thread. Interrupt method throws a ThreadInterruptException. Essentially, a call to the Thread. Interrupt method interrupts the thread and throws a ThreadInterruptedException to interrupt the thread inside of a blocking call.

Which is the exception thrown when a thread is aborted?

When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException on . NET Framework. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.


Video Answer


2 Answers

Use thread.Interrupt(); instead of Abort method.

like image 91
M Komaei Avatar answered Sep 29 '22 16:09

M Komaei


Without aborting the only solution is to poll the cancellation request often enough so after all the while (!canceled) solution you mentioned.

The examples I saw are using while (!canceled) loops, which cant stop long operations (like Thread.Sleep(1000000).

This is just partially true. For example, this can be re-written like this to be responsive:

 var timeout = TimeSpan.FromSeconds(60);  var stopwatch = new Stopwatch();  stopwatch.Start();   while (!cancelToken.IsCancellationRequested   && stopwatch.ElapsedMilliseconds < timeout) {     Thread.Sleep(10); } 

Of course, not every task can be easily re-written to poll the cancellation like this. If you are in a deep call chain it can be a pain to check the cancellation at every level. For that reason you can also use the CancellationToken.ThrowIfCancellationRequested method, which will throw an OperationCanceledException if there was a cancel request. I usually tend to not throwing an exception just for myself and using it for control flow but cancellation is one of the areas where it can be justified.

This is solution has of course some limitations compared to Abort:

  • You will not able to cancel 3rd party routines, which don't support cancellation and you cannot refactor them
  • The OperationCanceledException can be swallowed easily, whereas ThreadAbortException was always re-raised at the end of the catch blocks so a 3rd part library could be aborted by a good chance even if contained general catch blocks.

Update:

If you are confident/desperate enough you can use the ThreadEx.Abort method, which calls the Thread.AbortInternal by reflection. Though it is not guaranteed it will be a long-living solution in .NET Core.

Though I don't completely agree with making Thread.Abort obsolete as it was a good last-chance tool for shutting down routines on which you didn't have influence otherwise, I'm also at the side abortion must be avoided at all costs as it can have nasty side effects. If you are the author of the whole code base it can be always avoided.

Update 2:

It seems that AbortInternal has been removed since then. At least current .NET Core source does not contain such a method.

like image 21
György Kőszeg Avatar answered Sep 29 '22 16:09

György Kőszeg