实现IEnumerable包括async / await Task

时间:2017-07-15 01:20:46

标签: c# async-await task

我想创建一个容器,每次我想要获取其项目(迭代),将项目延迟返回我真的希望实现多个延迟的延迟(在返回每个项目后延迟定时器+延迟+返回一些项目后延迟)。

但我在以下示例中简化了这个想法。 以下代码有效,但在使用await Task.Delay(...)时出现问题。我在代码中描述它(请阅读评论)。

问题:

  • 有没有办法用GetEnumerator方法实现它,因为阻止阻塞迭代MyContainer object的线程?
  • 你有什么有趣的方法来实现这个想法(任何有趣的解决方案)? :)
  

我认为很明显我想在IEnumerable<T>中隐藏和嵌入延迟机制。

     

我不会在我的高级别课程中对foreach包含延迟进行简单的List<int>

     

我想制作一个带有嵌入延迟机制的容器,最后只需使用foreach而不是MyContainer,而不会有任何延迟(如波纹管测试)。

class MyContainer : IEnumerable<int>
{
    private readonly int _sec;
    private readonly List<int> _items;
    public MyContainer(List<int> items, int sec)
    {
        this._sec = sec;
        this._items = items;
    }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _items.Count(); ++i)
        {
            //-----------------------------------------------------------------
            // It works but block caller thread (E.g. block UI Thread)
            Thread.Sleep(Sec*1000);

            //-----------------------------------------------------------------
            //Problem line:
            //Because return type of current method should be async Task<...>
            //And it seems «yield» has problem with Task methods
            //await Task.Delay(_sec*1000); // <<<  Error line

            //-----------------------------------------------------------------
            yield return _items[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
上述代码的

测试 Thread.Sleep(不含await Task.Delay):

var myCnt=new MyContainer(new List<int>() {10,20,30,40,50}, 2);
var sw=new Stopwatch();
sw.Start();

foreach (var item in myCnt)
{
    Trace.WriteLine(sw.Elapsed);
}

sw.Stop();

// Result:
// 00:00:02.0014343
// 00:00:04.0034690
// 00:00:06.0045362
// 00:00:08.0056571
// 00:00:10.0067891

1 个答案:

答案 0 :(得分:0)

这应该是概念上的大部分方式,你可以调整它。

这个想法是它没有阻塞,但是可以通过可控制的延迟等待每个产生的值。

public static IEnumerable<Task<T>> EnumerateWithDelay<T>(Func<int, T> generator, Func<int, T, bool> limiter, TimeSpan delay)
{
    var idx = 0;
    while (true)
    {
        var item = generator(idx);
        if (!limiter(idx, item)) yield break;
        yield return Task.Delay(delay).ContinueWith(_ => item);
        idx++;
    }
}

public async Task A()
{
    foreach (var itemTask in EnumerateWithDelay(idx => idx, (idx, val) => idx < 10, TimeSpan.FromSeconds(0.5)))
    {
        // I'll take .5 seconds
        var number = await itemTask;
    }
}

经过一些调整后,您可以在延迟结束之前调用生成器功能。