美文网首页
【C#】关于异步循环(IAsyncEnumerable)的超时时

【C#】关于异步循环(IAsyncEnumerable)的超时时

作者: 冰麟轻武 | 来源:发表于2022-10-25 12:27 被阅读0次

    今天在做异步循环的时候发现一个很有意思的事情
    先看代码

    public static async IAsyncEnumerable<string> Test([EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        for (int i = 0; i < n ; i++)
        {
            await Task.Delay(1000, cancellationToken);
            yield return i.ToString();
        }
    }
    

    这里模拟一个异步循环,每次循环延迟1秒,n表示不确定会循环多少次,有一个参数是取消标记

    下面调用这个方法

    var source = new CancellationTokenSource(3000);
    await foreach (var item in Test().WithCancellation(source.Token))
    {
        Console.WriteLine(item);
    }
    

    执行结果


    打印2次后超时报错

    这是个很有啥意思的地方是:大部分情况下我不确定会循环多少次,所以没办法给定一个固定的超时时间
    但我希望每次不超过1.5秒,使用CancellationTokenSource.TryReset()可达到这个效果
    代码如下:

    var source = new CancellationTokenSource(1500);
    await foreach (var item in Test().WithCancellation(source.Token))
    {
        source.TryReset();
        Console.WriteLine(item);
    }
    Console.ReadLine();
    

    确实可以完整输出


    但是,如果我又希望他可以控制总时间怎么办呢?
    很简单再加一个 CancellationTokenSource

    代码如下:

    var step = new CancellationTokenSource(1500);
    var total = new CancellationTokenSource(5000);
    var source = CancellationTokenSource.CreateLinkedTokenSource(step.Token, total.Token);
    await foreach (var item in GitHelper.Test().WithCancellation(source.Token))
    {
        step.TryReset();
        Console.WriteLine(item);
    }
    Console.ReadLine();
    

    输出:


    总时间超过5秒超时

    单次执行时间不足1.5秒所以每次单步都不超时,但总时间超过了5秒所以总时间超时了
    达到了预期的效果

    为了严谨,修改下单步时间试试


    直接超时了

    但是!对于异步循环来说这是一个非常常见的场景,所以把他封装成一个扩展方法方便调用

    public static async IAsyncEnumerable<string> WithTimeout(this IAsyncEnumerable<string> enumerable, int totalMilliseconds, int stepMilliseconds = 0)
    {
        using var step = stepMilliseconds <= 0 ? null : new CancellationTokenSource(stepMilliseconds);
        using var total = totalMilliseconds <= 0 ? null : new CancellationTokenSource(totalMilliseconds);
        using var source = total.LinkedTokenSource(step);
    
        await foreach (var item in enumerable.WithCancellation(source?.Token ?? default))
        {
            step?.TryReset();
            yield return item;
        }
    }
    
    public static CancellationTokenSource? LinkedTokenSource(this CancellationTokenSource? source1, CancellationTokenSource? source2)
    {
        if (source1 is not null && source2 is not null)
        {
            return CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);
        }
    
        return source1 ?? source2;
    }
    

    实际使用中调用WithTimeout方法,设置参数,第一个参数表示总超时时间,第二个参数表示单步超时时间
    调用也很方便

    await foreach (var item in Test().WithTimeout(5000, 1500))
    {
        Console.WriteLine(item);
    }
    Console.ReadLine();
    
    ``

    相关文章

      网友评论

          本文标题:【C#】关于异步循环(IAsyncEnumerable)的超时时

          本文链接:https://www.haomeiwen.com/subject/sebnzrtx.html