I have a framework which creates a CancellationTokenSource, configures CancelAfter, then calls an async method and passes the Token. The async method then spawns many tasks, pas
Provide the CancellationToken
to Task.Run
in addition to passing it to the method doing the work. When you do this Task.Run
can see that the exception thrown was caused by the CancellationToken
it was given, and will mark the Task
as cancelled.
tasks.Add(Task.Run(() => this.DoWork(work, cancelationToken),
cancelationToken));
Once you've done this you can ensure that DoWork
throws when the token is cancelled, rather than checking IsCancellationRequested
to try to end by being marked as "completed successfully".
I recommend that you follow the standard cancellation pattern of throwing an exception rather than just returning:
public static void DoWork(work, cancellationToken)
{
while (work.IsWorking)
{
cancellationToken.ThrowIfCancellationRequested();
work.DoNextWork();
}
}
If you have cleanup work to do, that's what finally
is for (or using
, if you can refactor that way):
public async Task Run(CancellationToken cancellationToken)
{
HashSet<Task> tasks = new HashSet<Task>();
foreach (var work in this.GetWorkNotPictured)
{
tasks.Add(Task.Run(() => this.DoWork(work, cancellationToken))
}
try
{
await Task.WhenAll(tasks);
}
finally
{
this.CleanUpAfterWork();
}
}