常規迭代器塊(即"收益迴報")是否与"異步"和"等待"不相容?
這很好地說明了我要做什麼:
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;
}
但是,我收到一个編譯器錯誤,指出"無法从資源中載入訊息字元串"。
這是另一種尝試:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
但是,再次,編譯器返迴錯誤:"無法从資源中載入訊息字元串"。
這是我專案中真正的程式設計代碼
当我有一个列表任務時,這非常有用,该任務可以从URL下載HTML
我使用語法" yield return await task",結果是我想要
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;
}
但似乎我必须這樣做。
感谢您的帮助。
- 5月前1 #
- 5月前2 #
tl;dr 用yield實施的迭代器是一个阻塞結構,因此到目前為止,wait和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
的每个值 ?我认為第二个是所需的行為,在這種情况下,現有的Iterator語義將無法工作.維兹威兹 介面基本上是
IEnumerator<T>
我無视
public interface IEnumerator<T> T Current; bool MoveNext(); }
因為它對於一系列異步結果没有意義.但是您需要的是這樣的东西:Reset()
当然,
public interface IAsyncEnumerator<T> T Current; Task<bool> MoveNext(); }
也無法与此配合使用,您必须像這樣手動进行迭代:foreach
- 5月前3 #
根据
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(); }
的新功能 (鏈接1和鏈接2),我们將得到 C# 8.0 介面支援,將允许您进行第二次尝試.看起来像這樣:IAsyncEnumerable<T>
我们可以在
async Task<Foo> DoSomethingAsync(string url) { ... } // producing IAsyncEnumerable<T> async IAsyncEnumerable<Foo> DownLoadAllURL(string[] strs) { foreach (string url in strs) { yield return await DoSomethingAsync(url); } } ... // using await foreach (Foo foo in DownLoadAllURL(new string[] { "url1", "url2" })) { Use(foo); }
中實現相同的行為 但語義不同:C# 5
Brian Gideon的迴答意味着,呼叫代碼將異步获取並行获得的結果的集合.上面的代碼暗示呼叫代碼將以異步方式从一个流中一个接一个地获得結果。
- 5月前4 #
我知道我對答案已经来不及了,但這是另一个簡單的解決方案,可以 该庫可以實現:
GitHub:https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org:https://www.nuget.org/packages/AsyncEnumerator/
它比Rx簡單得多。async Task<Foo> DoSomethingAsync(string url) { ... } IEnumerable<Task<Foo>> DownLoadAllURL(string[] strs) { foreach (string url in strs) { yield return DoSomethingAsync(url); } } // using foreach (Task<Foo> task in DownLoadAllURL(new string[] { "url1", "url2" })) { Foo foo = await task; Use(foo); }
- 5月前5 #
有計划要做
https://github.com/dotnet/csharplang/issues/43
但是目前不可能
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); }); }
相似問題
- c#:並行執行任務c#netasynchronousasyncawaittaskparallellibrary2021-01-11 04:56
您所描述的內容可以通過
Task.WhenAll
完成 方法.請註意,代碼是如何變成簡單的單行代碼的.發生的情况是每个單独的URL開始下載,然後WhenAll
用於將這些操作合並為一个Task
可以等待。