簡體   English   中英

C# 帶取消令牌的異步/並行編程

[英]C# Async/Parallel Programming with Cancellation Tokens

我有一個實現IService接口的服務列表。 我還有一個實現ICalculator接口的計算服務。

服務

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

計算器

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

我希望所有實現IService的服務並行運行IService.GetSomeDataAsync()並且ICalculator.CalculateAsync()在每個服務的結果上運行。

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;
}

這一切都按預期工作。 x.Resulto.Result的調用不會鎖定線程,因為任務在調用時已完成。 當我嘗試實現取消令牌時,問題就出現了。 我希望能夠在需要很長時間時取消Iservice.GetSomeDataAsync() 我也不言而喻,如果IService.GetSomeDataAsync()被取消,那么ICalculator.CalculateAsync()也不應該運行。

我將CancellationTokenSource設置為在 10 秒內取消。 這有效,但它拋出了我認為是OperationCanceledException但我無法繼續執行程序或捕獲錯誤。

IService.GetSomeDataAsync()方法看起來像這樣。

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;
}

*請注意,我將 Flurl 用於 web 請求。 我將tokenSource.Token傳遞給通過包含方法參數傳入的方法。

ICalculator.CalculateAsync()方法看起來像這樣。

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;
}

*請注意,我將 Flurl 用於 web 請求。 我將tokenSource.Token傳遞給通過包含方法參數傳入的方法。

所以問題是我如何正確使用取消令牌來達到預期的結果?

這個解決方案對我有用。

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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM