我可以等待一个枚举我有发电机创建? [英] Can I await an enumerable I create with a generator?

查看:84
本文介绍了我可以等待一个枚举我有发电机创建?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有一个整数序列我获得异步。

Let's say I have a sequence of integers I obtain asynchronously.

async Task<int> GetI(int i){
    return await Task.Delay(1000).ContinueWith(x => i);
}

我要创建了该序列发生器,如果序列是同步我会做:

I want to create a generator over that sequence, if the sequence was synchronous I'd do:

IEnumerable<int> Method()
{
    for (var i = 0; i < 100; i++)
    {
        yield return GetI(i); // won't work, since getI returns a task
    }
}

所以,我想这个比喻是使发电机异步,并从它产生:

So, I figured the analogy is making the generator async and yielding from it:

async Task<IEnumerable<int>> Method()    
{
    for (var i = 0; i < 100; i++)
    {
        yield return await Task.Delay(1000).ContinueWith(x => i);
    }
}

这是行不通的,因为与产量的方法必须返回一个的东西了IEnumerable ,替代,这使得更多的意义是的IEnumerable&LT;任务&LT; INT&GT;&GT; 但因为,将不能编译异步方法必须返回工作 s或无效的。

This won't work, since a method with yield must return an IEnumerable of something, the alternative, which makes more sense is IEnumerable<Task<int>> but that won't compile since async methods must return Tasks or void.

现在,我知道我可以简单地删除的await并返回的IEnumerable&LT;任务&LT; INT&GT;&GT; 但不会帮我,因为迭代将不断地问数据之前,它的任何准备,所以它并没有解决我的问题。

Now, I realize I can simply remove the await and return an IEnumerable<Task<int>> but that won't help me since the iteration will keep asking for data before any of it is ready, so it doesn't solve my issue.


  • 有没有办法很好地混合可枚举和任务,并具有很好的糖语言给我的await和屈服?

  • 有没有办法很好地使用它?

(从网上搜索,我怀疑回答的第一个问题是假的,第二个是一个观察者/可观察到的,但我找不到任何标准基准,我感兴趣的最好办法C#实现这个模式)

推荐答案

异步序列是有趣。有许多不同的方法,这取决于你想要做什么。我不是您想要的语义完全清楚,所以这些都是一些选项。

Asynchronous sequences are interesting. There's a number of different approaches, depending on exactly what you want to do. I'm not entirely clear on your desired semantics, so these are some of the options.

任务&LT;&IEnumerable的LT; T&GT;&GT; 是一个异步检索集合。只有一个任务 - 酮异步操作 - 检索整个集合。这听起来并不像它就是你想要的。

Task<IEnumerable<T>> is an asynchronously-retrieved collection. There is only one task - one asynchronous operation - that retrieves the entire collection. This does not sound like it's what you want.

的IEnumerable&LT;任务&LT; T&GT;&GT; 是(异步)数据(同步)序列。有多个任务,这可能会或可能不会全部同时处理。有几个用于实现这个选项。一种是使用一个枚举块,并产生任务;这种方法将在每次下一个项目从枚举检索时间开始一个新的异步操作。或者,您可以创建并返回任务的集合,所有任务运行的同时(这可以优雅地在一个源序列通过LINQ的选择做然后按了ToList / 的ToArray )。但是,这有两个缺点:没有办法以异步方式确定顺序已经结束,这并不容易立即启动的下次的项目处理返回当前项目(这是常见的后预期的行为)。

IEnumerable<Task<T>> is a (synchronous) sequence of (asynchronous) data. There are multiple tasks, which may or may not all be processing simultaneously. There are a couple of options for implementing this. One is using an enumerator block and yielding tasks; this approach will start a new asynchronous operation each time the next item is retrieved from the enumerable. Alternatively, you can create and return a collection of tasks with all tasks running concurrently (this can be done elegantly over a source sequence via LINQ's Select followed by ToList/ToArray). However, this has a couple of drawbacks: there is no way to asynchronously determine if the sequence is already ended, and it's not easy to immediately start the next item processing after returning the current item (which is commonly desired behavior).

问题的核心是,的IEnumerable&LT; T&GT; 本质上是同步的。有一对夫妇的解决方法。一个是 IAsyncEnumerable&LT; T&GT; ,这是一个异步相当于的IEnumerable&LT; T&GT; 和可用的的 IX-异步的NuGet包。这种方法有其自身的缺点,虽然。当然,你失去了的IEnumerable&LT漂亮的语言支持; T&GT; (即普查员块和的foreach )。此外,非同步枚举的想法是不完全的高性能;理想情况下,异步的API应该是粗短而不是健谈,而且可枚举非常健谈。在original设计这里,并在chunky/chatty这里考虑。

The core problem is that IEnumerable<T> is inherently synchronous. There are a couple of workarounds. One is IAsyncEnumerable<T>, which is an asynchronous equivalent of IEnumerable<T> and available in the Ix-Async NuGet package. This approach has its own drawbacks, though. Of course, you lose the nice language support for IEnumerable<T> (namely, enumerator blocks and foreach). Also, the very notion of an "asynchronous enumerable" is not exactly performant; ideally, asynchronous APIs should be chunky rather than chatty, and enumerables are very chatty. More discussion on the original design here, and on the chunky/chatty considerations here.

因此​​,这些天一个更常见的解决方案是使用观测数据流(两者也可以通过的NuGet)。在这种情况下,你必须要考虑的序的东西与自己的生活。观测值都推为主,因此消费code是(理想)的反应。数据流有一个演员的感觉,所以他们的行为更独立,再次推结果消费code。

So, these days a much more common solution is to use observables or dataflows (both also available via NuGet). In these cases, you have to think of the "sequence" as something with a life of its own. Observables are push-based, so the consuming code is (ideally) reactive. Dataflows have an actor feel, so they act more independent, again pushing results to the consuming code.

这篇关于我可以等待一个枚举我有发电机创建?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆