Search code examples
c#cancellation

C#: Async Task: Cancel


I have an function that does some work for a few seconds. I'm trying to cancel that task after a certain time period and am unable to do so. My objective is to do the asyncFunction for 5 seconds and then, move onto to a different task. For some reason, I can't seem to await _t1.

private async Task asyncFunction(string line_1, string line_2, string line_3, string line_4, int _time, System.Threading.CancellationTokenSource _cts)
    {
        _cts.Token.ThrowIfCancellationRequested();

        await Task.Run(async () => {
            someFunction_01(line_1, line_2, line_3, line_4);                
            someFunction_01("HELLO WORLD");
            await Task.Delay(_time, _cts.Token);
        });
    }

The way I'm calling my funcion is:

TimeSpan _timeout = TimeSpan.FromSeconds(5);
            var timeoutCancellationTokenSource = new System.Threading.CancellationTokenSource(_timeout);
            
            Task _t1 = asyncFunction("LINE 01", " ", " ", " ", 6000, timeoutCancellationTokenSource);
if (await Task.WhenAny(_t1, Task.Delay(_timeout, timeoutCancellationTokenSource.Token)) == _t1)
            {                    
                await _t1;
            }
            else
            {
                // timeout/cancellation logic                    
                timeoutCancellationTokenSource.Cancel();
            }

I can't seem to cancel the Task.Delay inside of the asyncFunction. Any code help is appreciated.


Solution

  • I don't quite understand the purpose of the code. Particulary the if-statement:

    if (await Task.WhenAny(_t1, Task.Delay(_timeout, timeoutCancellationTokenSource.Token)) == _t1)
    

    Why pass in a _timeout to Task.Delay which is the same timeout as you created the timeoutCancellationTokenSource with?

    If we ignore the first parameter you give Task.Delay it also means you have two calls to Task.Delay with the same CancellationToken and now you have a race-condition where it's impossible to predict which Task.Delay will be cancelled first.


    To show an example of how you can get the cancellation to work, you can do like this:

    TimeSpan _timeout = TimeSpan.FromSeconds(1);
    var timeoutCancellationTokenSource = new System.Threading.CancellationTokenSource(_timeout);
    
    Task _t1 = asyncFunction("LINE 01", " ", " ", " ", 2000, timeoutCancellationTokenSource);
    try
    {
        await _t1;
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Operation was cancelled");
    }
    
    // Output: Operation was cancelled
    

    If _timeout is longer than the 2000 milliseconds passed into asyncFunction the task completes after 2 seconds without being cancelled.

    See this fiddle for a test run.