Answered by:
Cancelling a Task out of Several tasks

Question
-
I have a program where 1000 tasks are created using the factory, and I have set the schedule to only run 5 at a time. sometimes one of these tasks will "hang" and will run for longer time than what it should normally take.
I could not find anywhere within TPL for .Net 4.0 how to track the time each task is taking while it's in the running state mode (if I know when it started I can track how long it has been running for).
So I created a separate list that tracks each task by taskid and when it was observed in the running state mode. and later if it has been running for more than a minute (too much time), I want to cancel this single task.
The cancellation token that is passed to the startnew function below is for cancelling all tasks in case the user wanted to stop the whole operation.
factory.StartNew(AddressOf library.functionName, _aCancelationToken)
so I cannot use this token for canceling the task, otherwise it will cancel all tasks.
I also thought about Linking multiple Cancellation Token but it will yield the same behaviour in the previous line.
I also tried the following:
When I find a task that has been running for unreasonable time, I do a wait on it for 0 milliseconds and I pass a brand new cancellation token that is canceled, I thought it will stop the task but it does not:
Dim ctSource_ForceSingle As New CancellationTokenSource
Dim ct_ForceSingle As CancellationToken = ctSource_ForceSingle.Token
ctSource_ForceSingle.Cancel()
hangingTask.Wait(0, ct_ForceSingle)hangingTask is a task object.
Not sure where to go from here ...
Wednesday, November 24, 2010 4:19 PM
Answers
-
Ah, I misunderstood your requirements. Sure, you can create the timed cancellation token once the task starts running, passing that token down to any called functions. You'll just want to have a try/catch in your task body to deal with OperationCanceledExceptions that may emerge.
- Marked as answer by Stephen Toub - MSFTMicrosoft employee, Moderator Tuesday, December 7, 2010 1:25 AM
Tuesday, December 7, 2010 1:24 AMModerator
All replies
-
Hi Tamer-
The desire to "timeout" a task by automatically canceling it after a certain period of time is a common request and is something we're planning to address in the next release. For now, an easy solution is for you to use a function like this:
private static CancellationToken CreateTimedToken(int millisecondsTimeout)
{
var cts = new CancellationTokenSource();
new Timer(self => { ((IDisposable)self).Dispose(); cts.Cancel(); }).Change(millisecondsTimeout,-1);
return cts.Token;
}and then use a call to this method in your calls to StartNew, e.g.
var ct = CreateTimedToken(10000));
Task.Factory.StartNew(() => foo(ct), ct);This will create a CancellationToken that will have cancellation requested after 10 seconds, and will associate that token with the created task, both by passing it into your function and by passing it into StartNew. The latter will ensure that the task will be canceled if the cancellation request is received prior to the task starting to run, and the former enables your function to deal with the cancellation request appropriately. Note that cancellation does not forcibly interrupt what the task is doing; that would be akin to a Thread.Abort, which is considered to be very heavy handed and dangerous, as it can leave your AppDomain's state with invariants broken. That said, if you really, really know what you’re doing and do want to use a Thread.Abort, you can hook up the thread abort to the cancellation with code something like:
Task.Factory.StartNew(() =>
{
using(ct.Register(Thread.CurrentThread.Abort)) functionName();
}, ct);Again, I do recommend against this, unless you’re tearing down the AppDomain and thus ridding yourself of the state that could have been corrupted by using an abort.
- Proposed as answer by Stephen Toub - MSFTMicrosoft employee, Moderator Saturday, November 27, 2010 6:19 PM
Saturday, November 27, 2010 6:19 PMModerator -
Thanks Stephen, there is an issue when I apply the approach above:
I do Task.Factory.StartNew() for all 1000 tasks at the same time, hence the timers for all tasks will start at the same time, and will give me only 10 seconds for the whole job, so unfortunately this approach won't work. I do use a schedule class to have only 5 running tasks.
But I used your idea to come up with a different solution that I think is working, I did test it on one task and it worked nice, here are the steps I have done:
1- before creating each task, I create a cancellation token source and cancellation token
2- The task I'm going to run is encapsulated in a library object, so what I did is I passed the cancellation token source to that library object instance (before scheduling the task, i.e before calling the factory.startnew()).
3- I called factory.startnew() and I passed to it the cancellation token.
4- The previous steps are all done for all 1000 tasks, so I end up with 1000 cancellation token source that are assigned to each task (one to one).
5- Each task now can do a self-destruction since it has access to the cancellation token source. And that's exactly what I do, in the main entry point for the task (gets executed when the task is in running mode), I do call the timer class to set the cancellation token source.cancel method after 10 seconds, so it effectively assigns itself 10 seconds from the time it started regardless of how long it was in the "Waiting to run" state.
I'm continuing to test on large number of tasks, will let you know if it does not work.
and I'm anxious to see the next release to address this requirement!
-TamerMonday, November 29, 2010 11:36 PM -
Ah, I misunderstood your requirements. Sure, you can create the timed cancellation token once the task starts running, passing that token down to any called functions. You'll just want to have a try/catch in your task body to deal with OperationCanceledExceptions that may emerge.
- Marked as answer by Stephen Toub - MSFTMicrosoft employee, Moderator Tuesday, December 7, 2010 1:25 AM
Tuesday, December 7, 2010 1:24 AMModerator -
Ah, I misunderstood your requirements. Sure, you can create the timed cancellation token once the task starts running, passing that token down to any called functions. You'll just want to have a try/catch in your task body to deal with OperationCanceledExceptions that may emerge.
Exactly!!!! and passing the cancellation token source to the task itself is the key to achieve this self-destruction method.
-TamerTuesday, December 7, 2010 6:38 PM