Parallel.ForEach y async-await [duplicado]

Resuelto Sergino asked hace 10 años • 0 respuestas

Tenía ese método:

public async Task<MyResult> GetResult()
{
    MyResult result = new MyResult();

    foreach(var method in Methods)
    {
        string json = await Process(method);

        result.Prop1 = PopulateProp1(json);
        result.Prop2 = PopulateProp2(json);

    }

    return result;
}

Entonces decidí usar Parallel.ForEach:

public async Task<MyResult> GetResult()
{
    MyResult result = new MyResult();

    Parallel.ForEach(Methods, async method =>
    {
        string json = await Process(method);    

        result.Prop1 = PopulateProp1(json);
        result.Prop2 = PopulateProp2(json);
    });

    return result;
}

Pero ahora tengo un error:

Un módulo o controlador asincrónico se completó mientras aún estaba pendiente una operación asincrónica.

Sergino avatar Apr 17 '14 22:04 Sergino
Aceptado

asyncno funciona bien con ForEach. En particular, su asynclambda se está convirtiendo en un async voidmétodo. Hay varias razones para evitarloasync void (como lo describo en un artículo de MSDN); uno de ellos es que no se puede detectar fácilmente cuándo se asyncha completado la lambda. ASP.NET verá que su código regresa sin completar el async voidmétodo y (apropiadamente) generará una excepción.

Lo que probablemente desee hacer es procesar los datos simultáneamente , pero no en paralelo . El código paralelo casi nunca debería usarse en ASP.NET. Así es como se vería el código con el procesamiento concurrente asíncrono:

public async Task<MyResult> GetResult()
{
  MyResult result = new MyResult();

  var tasks = Methods.Select(method => ProcessAsync(method)).ToArray();
  string[] json = await Task.WhenAll(tasks);

  result.Prop1 = PopulateProp1(json[0]);
  ...

  return result;
}
Stephen Cleary avatar Apr 17 '2014 17:04 Stephen Cleary

.NET 6 finalmente agregó Parallel.ForEachAsync , una forma de programar trabajo asincrónico que le permite controlar el grado de paralelismo:

var urlsToDownload = new [] 
{
    "https://dotnet.microsoft.com",
    "https://www.microsoft.com",
    "https://twitter.com/shahabfar"
};

var client = new HttpClient();

var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
await Parallel.ForEachAsync(urlsToDownload, options, async (url, token) =>
{
    var targetPath = Path.Combine(Path.GetTempPath(), "http_cache", url);

    var response = await client.GetAsync(url, token);
    // The request will be canceled in case of an error in another URL.

    if (response.IsSuccessStatusCode)
    {
        using var target = File.OpenWrite(targetPath);

        await response.Content.CopyToAsync(target);
    }
});
Majid Shahabfar avatar Aug 24 '2021 04:08 Majid Shahabfar