c# - ¿Es posible "esperar rendimiento rendimiento DoSomethingAsync()"
async-await c#-5.0 (7)
¿Los bloques de iterador regulares (es decir, "retorno de rendimiento") son incompatibles con "asincrónico" y "aguardar"?
Esto da una buena idea de lo que estoy tratando de hacer:
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;
}
Sin embargo, aparece un error del compilador que dice "no se puede cargar cadena de mensaje de los recursos".
Aquí hay otro intento:
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
Pero, de nuevo, el compilador devuelve un error: "no se puede cargar cadena de mensajes de los recursos".
Aquí está el código de programación real en mi proyecto
Esto es muy útil cuando tengo una Tarea de lista, esa tarea se puede descargar HTML desde una URL y utilizo la sintaxis "devolver rendimiento aguardan tarea", el resultado es que quiero IEnumerable<Foo>
. No quiero escribir este código:
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;
}
Pero parece que tengo que hacerlo.
Gracias por cualquier ayuda.
El rendimiento no funciona con la espera, desafortunadamente. Pero esto es para lo que es Rx. Consulte https://msdn.microsoft.com/library/hh242985
En primer lugar, tenga en cuenta que las cosas Async no están terminadas. El equipo C # todavía tiene un largo camino por recorrer antes de que C # 5 sea lanzado.
Dicho esto, creo que es posible que desee recopilar las tareas que se están desencadenando en la función DownloadAllHtml
de una manera diferente.
Por ejemplo, puedes usar algo como esto:
IEnumerable<Task<string>> DownloadAllUrl(string[] urls)
{
foreach(var url in urls)
{
yield return DownloadHtmlAsync(url);
}
}
async Task<string> DownloadHtmlAsync(url)
{
// Do your downloading here...
}
No es que la función DownloadAllUrl
NO sea una llamada asincrónica. Pero puede hacer que la llamada asincrónica se implemente en otra función (es decir, DownloadHtmlAsync
).
La Biblioteca de tareas paralelas tiene las funciones .ContinueWhenAny
y .ContinueWhenAll
.
Eso se puede usar así:
var tasks = DownloadAllUrl(...);
var tasksArray = tasks.ToArray();
var continuation = Task.Factory.ContinueWhenAll(tasksArray, completedTasks =>
{
completedtask
});
continuation.RunSynchronously();
Esta solución funciona como se esperaba. Observe la parte 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;
}
}
}
Había un plan para hacer
https://github.com/dotnet/csharplang/issues/43
Pero actualmente no es posible
Lo que está describiendo se puede lograr con la Task.WhenAll
. Task.WhenAll
se Task.WhenAll
método. Observe cómo el código se convierte en un simple trazador de líneas. Lo que sucede es que cada url individual comienza a descargarse y luego, WhenAll
se usa todo, se combinan esas operaciones en una única Task
que puede esperarse.
Task<IEnumerable<string>> DownLoadAllUrls(string[] urls)
{
return Task.WhenAll(from url in urls select DownloadHtmlAsync(url));
}
Sé que ya llegué tarde con la respuesta, pero aquí hay otra solución simple que se puede lograr con esta biblioteca:
GitHub: https://github.com/tyrotoxin/AsyncEnumerable
NuGet.org: https://www.nuget.org/packages/AsyncEnumerator/
Es mucho más simple que 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);
});
}
tl; dr Iteradores tal como se implementa con el rendimiento son una construcción de bloqueo, por lo que en este momento esperan y el rendimiento son incompatibles.
Long Debido a que iterar sobre un IEnumerable
es una operación de bloqueo, llamar a un método marcado como async
todavía lo ejecutará de forma bloqueante, ya que tiene que esperar a que termine esa operación.
async Task<IEnumerable<Foo>> Method(String [] Strs)
{
foreach(var str in strs)
{
yield return await DoSomethingAsync( str)
}
}
El Method
espera mezcla significados. ¿Desea esperar hasta que la Task
tenga un IEnumerable
y luego bloquear al iterar sobre él? ¿O estás tratando de esperar cada valor del IEnumerable
?
Supongo que el segundo es el comportamiento deseado y en ese caso la semántica Iterator existente no funcionará. La IEnumerator<T>
es básicamente
public interface IEnumerator<T>
T Current;
bool MoveNext();
}
Estoy ignorando Reset()
ya que no tiene sentido para una secuencia de resultados asincrónicos. Pero lo que necesitarías es algo como esto:
public interface IAsyncEnumerator<T>
T Current;
Task<bool> MoveNext();
}
Por supuesto, foreach
tampoco funcionará con esto y tendrías que iterar manualmente así:
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();
}