实现一个 IEnumerable include async/await Task



我想创建一个容器,每次我想要获取其项目(迭代(时,它都会延迟将项目返回给我。真的,我想要多种延迟(计时器延迟+返回每件物品后的延迟+返回某些物品后的延迟(。

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

问题:

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

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

我不会对一个List<int>包括延迟我的高级课程的身体进行简单的foreach

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

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

这应该在概念上让你大部分时间,你可以调整它。

这个想法是它不是阻塞的,但是每个生成的值都可以以可控的延迟等待。

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

通过一些调整,您可以使生成器函数在延迟结束之前不被调用。

最新更新