是否有可能“等待回报DoSomethingAsync()”
常规的迭代器块(即“yield return”)与“asynchronous”和“等待”不兼容?
这给了我想要做的一个好主意:
async Task<IEnumerable<Foo>> Method(String [] Strs) { // I want to compose the single result to the final result, so I use the SelectMany var finalResult = UrlStrings.SelectMany(link => //i have an Urlstring Collection await UrlString.DownLoadHtmlAsync() //download single result; DownLoadHtmlAsync method will Download the url's html code ); return finalResult; }
但是,我得到一个编译器错误,引用“无法从资源加载消息string”。
这是另一个尝试:
async Task<IEnumerable<Foo>> Method(String [] Strs) { foreach(var str in strs) { yield return await DoSomethingAsync( str) } }
但同样,编译器返回一个错误:“无法从资源加载消息string”。
这是我的项目中真正的编程代码
这是非常有用的,当我有一个列表任务,该任务可以从URL下载HTML,我使用语法“yield return await任务”,结果是我想IEnumerable<Foo>
。 我不想写这个代码:
async Task<IEnumerable<String>> DownLoadAllURL(String [] Strs) { List<Foo> htmls= new ... foreach(var str in strs) { var html= await DownLoadHtmlAsync( str) htmls.Add(item) } return htmls; }
但似乎我必须。
谢谢你的帮助。
你所描述的可以用Task.WhenAll
方法来完成。 注意代码如何变成一个简单的单行程式。 会发生什么是每个单独的url开始下载,然后使用WhenAll
将这些操作组合成一个可以等待的Task
。
Task<IEnumerable<string>> DownLoadAllUrls(string[] urls) { return Task.WhenAll(from url in urls select DownloadHtmlAsync(url)); }
tl; dr迭代器与yield一起实现是一个阻塞构造,因此现在正在等待并且yield是不相容的。
Long由于遍历IEnumerable
是一个阻塞操作,调用标记为async
的方法仍然会以阻塞的方式执行它,因为它必须等待该操作完成。
async Task<IEnumerable<Foo>> Method(String [] Strs) { foreach(var str in strs) { yield return await DoSomethingAsync( str) } }
等待Method
混合含义。 你想等到Task
有一个IEnumerable
,然后阻止迭代它? 或者你是否试图等待IEnumerable
每个值?
我假设第二个是所需的行为,在这种情况下,现有的迭代器语义将无法正常工作。 IEnumerator<T>
接口基本上是
public interface IEnumerator<T> T Current; bool MoveNext(); }
我忽略了Reset()
因为它对于一系列asynchronous结果是没有意义的。 但是你需要的是这样的:
public interface IAsyncEnumerator<T> T Current; Task<bool> MoveNext(); }
当然, foreach
也不能用这个,你必须像这样手动迭代:
var moveNext = await asyncEnumerator.MoveNext(); while(moveNext) { // get the value that was fetche asynchronously var v = asyncEnumerator.Current; // do something with that value // suspend current execution context until next value arrives or we are done moveNext = await asyncEnumerator.MoveNext(); }
我知道我的答案为时已晚,但这里有另一个可以用这个库实现的简单解决scheme:
GitHub: https : //github.com/tyrotoxin/AsyncEnumerable
NuGet.org:https://www.nuget.org/packages/AsyncEnumerator/
这比Rx简单得多。
using System.Collections.Async; static IAsyncEnumerable<string> ProduceItems(string[] urls) { return new AsyncEnumerable<string>(async yield => { foreach (var url in urls) { var html = await UrlString.DownLoadHtmlAsync(url); await yield.ReturnAsync(html); } }); } static async Task ConsumeItemsAsync(string[] urls) { await ProduceItems(urls).ForEachAsync(async html => { await Console.Out.WriteLineAsync(html); }); }
首先,请记住,asynchronous的东西没有完成。 在C#5发布之前,C#团队还有很长的路要走。
这就是说,我认为你可能希望以不同的方式收集在DownloadAllHtml
函数中被触发的任务。
例如,你可以使用这样的东西:
IEnumerable<Task<string>> DownloadAllUrl(string[] urls) { foreach(var url in urls) { yield return DownloadHtmlAsync(url); } } async Task<string> DownloadHtmlAsync(url) { // Do your downloading here... }
不是说DownloadAllUrl
函数不是一个asynchronous调用。 但是,您可以在另一个函数(即DownloadHtmlAsync
)上实现asynchronous调用。
任务并行库具有.ContinueWhenAny
和.ContinueWhenAll
函数。
可以这样使用:
var tasks = DownloadAllUrl(...); var tasksArray = tasks.ToArray(); var continuation = Task.Factory.ContinueWhenAll(tasksArray, completedTasks => { completedtask }); continuation.RunSynchronously();
此解决scheme按预期工作。 注意await Task.Run(() => enumerator.MoveNext())
部分。
using (var enumerator = myEnumerable.GetEnumerator()) { while (true) { if (enumerator.Current != null) { //TODO: do something with enumerator.Current } var enumeratorClone = monitorsEnumerator; var hasNext = await Task.Run(() => enumeratorClone.MoveNext()); if (!hasNext) { break; } } }
不幸的是,收益率不能正常工作。 但这是Rx的目的。 查看https://msdn.microsoft.com/library/hh242985