拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 如何同时压缩两个IAsyncEnumerables?

如何同时压缩两个IAsyncEnumerables?

白鹭 - 2022-01-23 2061 0 0

我有两个要成对“压缩”的异步序列,为此我使用ZipSystem.Linq.Async包中运算子但是,至少对于我而言,该运算子的行为方式并不理想。它不是同时列举两个序列,而是按顺序列举它们,结果是延迟加起来。我的每个序列平均每秒钟发出一个元素,我预计组合序列也会每秒钟发出 zipped 对,但实际上我每 2 秒得到一对。下面是一个演示此行为的最小示例:

static async IAsyncEnumerable<int> First()
{
    for (int i = 1; i <= 5; i  ) { await Task.Delay(1000); yield return i; }
}

static async IAsyncEnumerable<int> Second()
{
    for (int i = 1; i <= 5; i  ) { await Task.Delay(1000); yield return i; }
}

var stopwatch = Stopwatch.StartNew();
await foreach (var pair in First().Zip(Second()))
    Console.WriteLine(pair);
Console.WriteLine($"Duration: {stopwatch.ElapsedMilliseconds:#,0} msec");

输出:

(1, 1)
(2, 2)
(3, 3)
(4, 4)
(5, 5)
Duration: 10,155 msec

在 Fiddle 上试试

有什么方法可以Zip让程序在 5 秒而不是 10 秒内完成这两个序列?我对具有理想行为的自定义运算子或官方软件包中的运算子组合感兴趣。

uj5u.com热心网友回复:

像这样的东西似乎有效:

public static async IAsyncEnumerable<(TFirst, TSecond)> Zip<TFirst, TSecond>(this IAsyncEnumerable<TFirst> first, IAsyncEnumerable<TSecond> second)
{
    await using var e1 = first.GetAsyncEnumerator();    
    await using var e2 = second.GetAsyncEnumerator();
    
    while (true)
    {
        var t1 = e1.MoveNextAsync().AsTask();
        var t2 = e2.MoveNextAsync().AsTask();
        await Task.WhenAll(t1, t2);
        
        if (!t1.Result || !t2.Result)
            yield break;
        
        yield return (e1.Current, e2.Current);
    }
}

dotnetfiddle.net查看

当然,这会遗漏诸如空检查之类的东西,因此可以进行一些改进:这留给读者作为练习。

我也不相信这Task.WhenAllbool r1 = await t1; bool r2 = await t2; if (!r1 || !r2) yield break;这里更好

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *