等待在单个线程中生成多个项目
(OBS:英语不是我的母语,我知道这个问题的标题很不好,但我尽力让问题本身清楚)
让我们假设我有一个 IEnumerable
包含许多项,并且每个 MoveNext() 都非常昂贵 - 假设 ts 是使用进行昂贵计算的 yield return
方法生成的。
看一下这段代码:
var enumerator = ts.GetEnumerator();
while (true) {
T t = await TaskEx.Run<T>(() => enumerator.MoveNext() ?
enumerator.Current :
null);
if (t == null) break;
t.DoSomeLightweigthOperation();
}
它异步使用集合ts
,而不会阻塞主线程(在我的例子中是一个 UI 线程)。问题是:它为 ts 中的每一项生成一个线程。有没有一种(简单)方法可以仅使用一个线程完成整个工作来完成同样的事情?
因此,为了让自己清楚,我想仅使用一个线程生成这些项目,但我需要获取某种 Task
集合(或任何其他具有 GetAwaiter< /code>),这样我就可以在生成这些项目后
await
。
(OBS: English is not my native language and I understand that the title of this question is far from good, but I tried my best to make the question itself clear)
Let's suppose I have a IEnumerable<T> ts
that has MANY items and that each MoveNext() is VERY expensive - let's say ts
was generated using a yield return
method that makes expensive computations.
Look at this piece of code:
var enumerator = ts.GetEnumerator();
while (true) {
T t = await TaskEx.Run<T>(() => enumerator.MoveNext() ?
enumerator.Current :
null);
if (t == null) break;
t.DoSomeLightweigthOperation();
}
It consumes the collection ts
asynchronously, without blocking the main thread (which, in my case is a UI thread). The problem is: it spawns one thread for each item in ts
. Is there a (simple) way to do the same thing using only ONE thread that does the entire job?
So, just to make myself clear, I want to generate these items using only one thread, but I need to get some kind of collections of Task
s (or any other class with a GetAwaiter
) so that I can await
after the generation of each of these items.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您现有的解决方案不会为每一项生成一个线程。相反,它创建一个线程池工作项,该工作项在线程池中排队,每个项目一次。每个
MoveNext
实际上有可能(甚至很可能)由同一个线程池线程完成。因此,我认为考虑到您的限制,您现有的解决方案将会起作用。
如果您可以更改枚举,我会考虑使用
IAsyncEnumerator
,它有一个TaskMoveNext() 成员。
IAsyncEnumerator
/IAsyncEnumerable
是 Ix_Experimental-异步。我还编写了一个几乎相同的IAsyncEnumerator
,它是 Nito.AsyncEx。异步枚举更接近您想要做的事情;有一个 Channel9 视频 描述了异步枚举(尽管此后 API 略有变化)。Your existing solution does not spawn one thread for each item. Rather, it creates a thread pool work item that is queued to the thread pool, once for each item. It is possible (even likely) that each
MoveNext
will actually be done by the same threadpool thread.So, I think your existing solution will work, given your constraints.
If you can change the enumerable at all, I'd consider
IAsyncEnumerator<T>
, which has aTask<bool> MoveNext()
member.IAsyncEnumerator<T>
/IAsyncEnumerable<T>
are part of Ix_Experimental-Async. I also wrote a nearly-identicalIAsyncEnumerator<T>
, which is part of Nito.AsyncEx. Asynchronous enumeration is a closer match to what you're trying to do; there's a Channel9 video that describes asynchronous enumeration (though the API has changed slightly since then).如果您想要做的只是在另一个线程上运行 ts,您可以执行以下操作:
它只是在线程池中的线程上运行它。
更新:所以该操作确实在 ui 工作,这意味着它需要在 ui 线程上排队。你没有说你使用哪个 ui 框架,但他们都有方法做到这一点。
例如,如果您使用的是 wpf,则可以使用 调度员
If all you want to do is run through ts on another thread you could do something like this:
Which just runs through it on a thread from the threadpool.
UPDATE: So the operation does ui work which means it needs to be queued on the ui thread. You don't say which ui framework you use but all of them have ways of doing that.
For instance if you are using wpf you can use the dispatcher