I encountered a problem how to properly cancel an asynchronous task.
Here is some draft.
My entry point runs two asynchronous task. The first task does some 'long' work and the second one cancels it.
Entry point:
private static void Main()
{
var ctc = new CancellationTokenSource();
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
var cancelationTask = Task.Run(() =>
{
Thread.Sleep(2000);
Console.WriteLine("[Before cancellation]");
ctc.Cancel();
});
try
{
Task.WaitAll(cancellable, cancelationTask);
}
catch (Exception e)
{
Console.WriteLine($"An exception occurred with type {e.GetType().Name}");
}
}
Method that returns cancel-able task:
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
Console.WriteLine("1st");
Thread.Sleep(1000);
Console.WriteLine("2nd");
Thread.Sleep(1000);
Console.WriteLine("3rd");
Thread.Sleep(1000);
Console.WriteLine("4th");
Thread.Sleep(1000);
Console.WriteLine("[Completed]");
}, token);
}
My purpose is to stop writing '1st','2nd','3rd' immediately after cancellation is called. But I get following results:
1st
2nd
3rd
[Before cancellation]
4th
[Completed]
For obvious reason I didn't get an exception that throws when cancellation is requested. So I tried to rewrite method as following:
private static Task ExecuteLongCancellableAdvancedMethod(CancellationToken token)
{
return Task.Run(() =>
{
var actions = new List<Action>
{
() => Console.WriteLine("1st"),
() => Console.WriteLine("2nd"),
() => Console.WriteLine("3rd"),
() => Console.WriteLine("4th"),
() => Console.WriteLine("[Completed]")
};
foreach (var action in actions)
{
token.ThrowIfCancellationRequested();
action.Invoke();
Thread.Sleep(1000);
}
}, token);
}
And now I got what I want:
1st
2nd
[Before cancellation]
3rd
An exception occurred with type AggregateException
But I guess creating of a collection of Action delegates and looping through it is not the most convenient way to deal with my problem.
So what's the proper way to do it? And why do I need to pass my cancellation token into Task.Run method as the second argument?