ThrowIfCancellationRequested() vs Abort() - c#

What am I trying to achieve?
I am trying to cancel a long running task from within another task using a CancellationToken. It's impossible for me to handle the cancellation and throw in the long running task because it will never touch the code that handles the cancellation (the task processes a bad regex pattern that takes forever anyway this is not important). I tried to handle from within another Task and poll for the cancellation request, and when I call ThrowIfCancellationRequested() it actually throws in that thread. So the long running task is still alive and hanging.
How I solved this
Well instead of using token's ThrowIfCancellationRequested() i acually called Abort() on the long running task's Thread and it works like charm.
And my question is: I am pretty sure it's not very elegant and I wanted to know if it is OK what I did there and how else can I approach this situation?

Thread.Abort is evil because it is highly dangerous. Cancellation in .NET (and any other platform I'm aware of) is cooperative. Either make the action cancel itself on demand or isolate it so that you can just ignore it.

Related

HostingEnvironment.QueueBackgroundWorkItem - Clarification?

I've read Stephen's article about fire and forget background actions in Asp.net.
It is not recommended to use Task.Run for fire-and-forget because Asp.net doesn't know that you've queued a task.
So if a recycle is about to occur, the task has no way of knowing it.
That's where HostingEnvironment.QueueBackgroundWorkItem gets in.
It will know that a recycle is about to happen and will invoke the Cancellation Token.
But!
FWIK - Background tasks are being "terminated" once the main thread has finished.
That means that if a request gets in (a new thread is being created/fetched) and it invokesTask.Run , and the response has finished (but Task has not) then the Task will be terminated.
Question:
Does QueueBackgroundWorkItem solve this problem ? or does it only exist to warn about recycle?
In other words, if there's a request which runs QueueBackgroundWorkItem and the response has finished, will the QueueBackgroundWorkItem continue to execute its code?
The docs say: " independent of any request", but I'm not sure if it answers my question though
According to the documentation, this method tries to delay application shutdown until background work has completed.
Differs from a normal ThreadPool work item in that ASP.NET can keep track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.
Also, it does not flow certain contexts which are associated with the current request and are inappropriate for request-independent background work:
This overloaded method doesn't flow the ExecutionContext or SecurityContext from the caller to the callee. Therefore, members of those objects, such as the CurrentPrincipal property, will not flow from the caller to the callee.
In ASP.NET there is no way to make sure that background work ever completes. The machine could blue screen, there could be a bug terminating the worker process, there could be a timeout forcing termination and many other things.
Or, your code could have a bug and crash. That causes the queued work to be lost as well.
If you need something executed reliably, execute it synchronously before confirming completion, or queue it somewhere (message queue, database, ...).
That means that if a request gets in (a new thread is being created/fetched) and it invokesTask.Run, and the response has finished (but Task has not) then the Task will be terminated.
No, Task.Run works independently of HTTP requests. In fact, there is no way to cancel a Task except if the task's code cancels itself.

How to wrap a single sync operation with a Task with a CancellationToken?

I have a single synchronous operation that could take a lot of time to complete. The caller of the operation provides a CancellationToken and the operation should be stopped immediately when the token is cancelled (within a few ms after cancellation would also work in this case).
How can I wrap this in a task with a CancellationToken?
I can't change the calling code nor the call itself.
What it used to be: LongOperation();
What I have now: await Task.Run(() => LongOperation(), cancellationToken).ConfigureAwait(false);
Clearly this doesn't work as you have to poll the token inside the action given to Task.Run.
I can't change the calling code nor the call itself.
Clearly this doesn't work as you have to poll the token inside the action given to Task.Run.
By far the easiest solution is to lift one of the requirements: either allow the CancellationToken to be ignored, or change the called code.
If that's really, truly, honestly not possible, then you'll need to run the code in another process. So, you'll need to kick off a child process that has access to that method, marshal all the arguments over to it, and then marshal back any result value or exception. Then, when the token is cancelled, kill the process.
There are less safe ways of doing the same thing: you can run the code in another AppDomain and shutdown the AppDomain on cancel, or you can run the code in another Thread and Abort the Thread on cancel. But both of those can easily cause resource leaks or application stability problems. The only truly safe way is a separate process.

Do you have to await async methods?

Lets say you have a service API call. The callee is somewhat performance critical, so in order not to hold up the API call any longer than necessary, there's an SaveAsync() method that is used. I can't await it, however, because that would hold up the API call just as long (or perhaps even longer) than the non-async version.
The reason I'm asking is this: If you don't await the call, is there a chance the Task object returned gets garbage collected? And if so, would that interrupt the running task?
The reason I'm asking is this: If you don't await the call, is there a chance the Task object returned gets garbage collected?
Generally, no, that shouldn't happen. The underlying TaskScheduler which queues the task, usually keeps a reference to it for the desired life-time until it completes. You can see that in the documentation of TaskScheduler.QueueTask:
A typical implementation would store the task in an internal data structure, which would be serviced by threads that would execute those tasks at some time in the future.
Your real problem is going to be with the ASP.NET SynchronizationContext, which keeps track of any on-going asynchronous operation at runtime. If your controller action finishes prior to the async operation, you're going to get an exception.
If you want to have "fire and forget" operations in ASP.NET, you should make sure to register them with the ASP.NET runtime, either via HostingEnvironment.QueueBackgroundWorkItem or BackgroundTaskManager
No, it won't interrupt the running task, but you won't observe the exceptions from the task either, which is not exactly good. You can (at least partially) avoid that by wrapping all running code in a try ... catch and log the exception.
Also, if you're inside asp.net, then your whole application could be stopped or recycled, and in this case your task will be interrupted. This is harder to avoid - you can register for AppPool shutdown notification, or use something like Hangfire.

Task equivalent that can be killed at once

I want to run a long running opeartion in the background.
The requirements are:
The operation should run async to the calling thread.
The calling thread can wait on the operation to complete and obtain its result
Upon timeout, the operation should be aborted at once.
I would have used task, but there is no mechanism that I know of to kill the task dead cold.
Cancel token is not suitable for me, I would only kill a task if it gets stuck for unknown reason - (a bug) , this is a fail-safe mechanism.
Needles to say if the task is stuck, there is no use in requesting cancel.
Same goes for BackgroundWorker.
Is there anything more elagent than using a shared object between the calling thread and a background thread?
There is nothing more elegant than using a shared object, since using a shared object is the elegant way of doing this :)
You cant provide a generic way of killing a task safely: Since the killer thread does not have any clue of what the killee is doing when trying to kill it, this would potentially leave your object model in a "corrupted" state.
Thread.Abort() has been created to do that the cleanest way possible: By throwing an exception (which allows "finally" statements to dispose used resources, or running transactions disposal on killed thread). But this method can make the code throw an exception in unexpected location. It is highly not recommended.
nb: Thread.Abort() does not work in any case (example: wont work if your thread is running native code via a P/Invoke for instance)
Thus, the elegant solution is to write clean code, which can decide when it wants to be killed (via a cancellation token).
nb2: The ultimate "Thread.Abort()" which will work in any case, and which which will keep things isolated: Create a new AppDomain, run your killable code in this AppDomain (via remoting), and call AppDomain.Unload() when you want to stop everything.
This is a quite extreme solution, though.
The only way to kill a thread 'dead cold' that I know of is Thread.Abort, however, you will see a lot of answers to this related question, Killing a Thread C#, indicating that it is generally bad practice to use it, except in rare occasions.
Another option is to avoid trying to kill the task dead cold and implement better error handling in your task such that it gracefully handles exceptions and situations where it 'gets stuck'.

Stopping all thread in .NET ThreadPool?

I am using ThreadPool in .NET to make some web request in the background, and I want to have a "Stop" button to cancel all the threads even if they are in the middle of making a request, so a simple bool wont do the job.
How can I do that?
Your situation is pretty much the canonical use-case for the Cancellation model in the .NET framework.
The idea is that you create a CancellationToken object and make it available to the operation that you might want to cancel. Your operation occasionally checks the token's IsCancellationRequested property, or calls ThrowIfCancellationRequested.
You can create a CancellationToken, and request cancellation through it, by using the CancellationTokenSource class.
This cancellation model integrates nicely with the .NET Task Parallel Library, and is pretty lightweight, more so than using system objects such as ManualResetEvent (though that is a perfectly valid solution too).
The correct way to handle this is to have a flag object that you signal.
The code running in those threads needs to check that flag periodically to see if it should exit.
For instance, a ManualResetEvent object is suitable for this.
You could then ask the threads to exit like this:
evt.Set();
and inside the threads you would check for it like this:
if (evt.WaitOne(0))
return; // or otherwise exit the thread
Secondly, since you're using the thread pool, what happens is that all the items you've queued up will still be processed, but if you add the if-statement above to the very start of the thread method, it will exit immediately. If that is not good enough you should build your own system using normal threads, that way you have complete control.
Oh, and just to make sure, do not use Thread.Abort. Ask the threads to exit nicely, do not outright kill them.
If you are going to stop/cancel something processing in another thread, ThreadPool is not the best choice, you should use Thread instead, and manage all of them in a container(e.g. a global List<Thread>), that guarantees you have full control of all the threads.

Categories