I have the code below that uses HttpClient to download content for a series of websites. However, whilst a synchronous inner function works fine in the Parallel.ForEach, I have been unable to get the asychronous version to work.
I would appreciate to know how to resolve this and how the code should look.
The code is a simple console app. However, I have been unable to get this to work at all (synchronous or async) in a Windows Form app. Can anyone explain why this is so?
Code:
using DLWeb;
using System.Collections.Concurrent;
// initialize test data
ConcurrentBag<Website> _websites = [ new Website("BBC", "https://www.bbc.co.uk"),
new Website("Google", "https://www.google.com"),
new Website("Amazon","https://www.amazon.com")];
// set mode
bool USE_DOWNLOAD_ASYNC = true;
string mode = USE_DOWNLOAD_ASYNC ? "Asynchronously" : "Synchronously";
Console.WriteLine($"Starting (Running Download {mode})...{Environment.NewLine}");
ParallelLoopResult _loopRes = new();
if (USE_DOWNLOAD_ASYNC) // Async
{
await Task.Run(() =>
{
_loopRes = Parallel.ForEach<Website>(_websites, async (item) =>
{
var res = await DownloadAsync(item);
item.Content = res;
});
});
}
else
{
await Task.Run(() =>
{
_loopRes = Parallel.ForEach<Website>(_websites, (item) =>
{
string s = "";
s = Download(item.URL);
item.Content = s;
});
});
}
if (_loopRes.IsCompleted)
{
foreach (var item in _websites)
{
Console.WriteLine($"{item.Name}: {item.Content.Length}{Environment.NewLine}");
}
Console.WriteLine("Done.");
}
Console.ReadKey();
// ----------------------------------------------------
static string Download(string site)
{
HttpClient httpClient = new();
var r = httpClient.GetStringAsync(site).Result;
return r;
}
async static Task<string> DownloadAsync(Website item)
{
HttpClient httpClient = new();
string res;
res = "" + await httpClient.GetStringAsync(item.URL);
return res;
}
The Website class:
public class Website
{
public string Name { get; set; } = "";
public string URL { get; set; } = "";
public string Content { get; set; } = "";
public Website()
{
}
public Website(string name, string url)
{
Name = name;
URL = url;
}
}
Any help appreciated.
Parallel.ForEachis not async-friendly. Look at the .NET 6Parallel.ForEachAsync.Parallel.ForEachAPI with an async delegate, and didn't work as well as they expected.Parallel.ForEachwithasync, we simply can't, because it's not possible. If you want us to show you how to adapt your code to use theParallel.ForEachAsyncinstead of theParallel.ForEach, this is something that we can do, but you have to ask for it explicitly. Before asking it, are you sure that theParallel.ForEachAsyncis available for your project? You have to target the .NET 6 or later to use it. What .NET platform and version are you targeting?Parallel.ForEach<Website>(_websites, async (item)suggested by the documentation and Visual Studio? Likewise, the synchronous code part works in a console app but not in Windows Forms. I have not been able to find out why.