简体   繁体   中英

C# Async/Parallel Programming with Cancellation Tokens

I have a list of services that implement the IService interface. I also have a calculation service that implements the ICalculator interface.

IService

public interface IService
{
    Task<List<Response>> GetSomeDataAsync(CancellationToken cancellationToken = default);
}

ICalculator

public interface ICalculator
{
    Task<List<Response>> CalculateAsync(Request request, CancellationToken cancellationToken = default);
}

I want all the services that implement IService to run IService.GetSomeDataAsync() in parallel and the ICalculator.CalculateAsync() to run on the results of each.

public async Task<List<Response>> Run()
{
    var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10));

    var data = await Task.WhenAll(_services.Select(service =>
    {
        var results = service.GetSomeDataAsync(tokenSource.Token);

        var newResults = results.ContinueWith(x =>
        {
            var request = new request
            {
                Data = x.Result
            };

            return _calculator.CalculateAsync(request, tokenSource.Token);
        }, tokenSource.Token);

        return newResults;
    }));

    var response = data.SelectMany(o => o.Result)
        .ToList();

    return response;
}

This all works as expected. The calls to x.Result and o.Result will not lock up the thread because the tasks are completed by the time its called. The issue comes when I try to implement a cancellation token. I want to be able to cancel Iservice.GetSomeDataAsync() when it is taking to long. I also goes with out saying that if IService.GetSomeDataAsync() gets cancelled then ICalculator.CalculateAsync() should not run either.

I set the CancellationTokenSource to cancel in 10 seconds. This works but it throws what I believe to be the OperationCanceledException but I am not able to move on with the program or catch the error.

The IService.GetSomeDataAsync() method looks something like this.

public async Task<List<Response>> GetSomeDataAsync(CancellationToken cancellationToken)
{
    try
    {
        var result = await "http://slowwly.robertomurray.co.uk/delay/100000/url/http://www.google.co.uk"
            .GetStringAsync(cancellationToken);
    }
    catch (OperationCanceledException x)
    {
        Console.WriteLine(x);
    }

    return result;
}

*Note that I am using Flurl for the web request. I pass the tokenSource.Token to the method that was passed in via the containing method parameters.

The ICalculator.CalculateAsync() method looks something like this.

public async Task<List<Response>> CalculateAsync(Request request, CancellationToken cancellationToken = default)
{
    try
    {
        var result = await "http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.co.uk"
            .GetStringAsync(cancellationToken);
    }
    catch (OperationCanceledException x)
    {
        Console.WriteLine(x);
    }

    return result;
}

*Note that I am using Flurl for the web request. I pass the tokenSource.Token to the method that was passed in via the containing method parameters.

So the question is how do I correctly work with the cancellation token to achieve the intended results?

This solution worked for me.

public async Task<List<Response>> Run()
{
    var data = await Task.WhenAll(_services.Select(async service =>
    {
        var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10));

        try
        {
            results = await service.GetSomeDataAsync(tokenSource.Token);
        }
        catch (Exception x)
        {
            return new List<Response>();
        }

        var request = new Request
        {
            Data = results
        };

        try
        {
            return await _calculator.CalculateAsync(request, tokenSource.Token);
        }
        catch (Exception x)
        {
            return new List<Response>();
        }
    }));

    var response = data
        .SelectMany(responses => responses)
        .ToList();

    return response;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM