我想创建一个容器,每次我想要获取其项目(迭代),将项目延迟返回。 我真的希望实现多个延迟的延迟(在返回每个项目后延迟定时器+延迟+返回一些项目后延迟)。
但我在以下示例中简化了这个想法。 以下代码有效,但在使用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
答案 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;
}
}
经过一些调整后,您可以在延迟结束之前调用生成器功能。