Здесь происходит то, что вы никогда не ждете завершения блока Parallel.ForEach
- вы просто возвращаете сумку, в которую он в конечном итоге закачается. Причина этого в том, что, поскольку Parallel.ForEach
ожидает Action
делегатов, вы создали лямбду, которая возвращает void
, а не Task
. Хотя методы async void
являются действительными, они, как правило, продолжают свою работу в новом потоке и возвращаются к вызывающей стороне, как только они await
Задача, и поэтому метод Parallel.ForEach
считает, что обработчик завершен, несмотря на то, чтооставшаяся работа переходит в отдельный поток.
Вместо этого используйте здесь синхронный метод;
Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, index =>
{
var response = client.GetAsync("posts/" + index).Result;
var contents = response.Content.ReadAsStringAsync().Result;
listResults.Add(contents);
Console.WriteLine(contents);
});
Если вам абсолютно необходимо использовать await
внутри, оберните его в Task.Run(...).GetAwaiter().GetResult()
;
Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, index => Task.Run(async () =>
{
var response = await client.GetAsync("posts/" + index);
var contents = await response.Content.ReadAsStringAsync();
listResults.Add(contents);
Console.WriteLine(contents);
}).GetAwaiter().GetResult();
В этом случае, однако, Task.run
обычно переходит в новый поток, поэтому мы подорвали большую часть контроля Parallel.ForEach;лучше использовать async
до конца;
var tasks = list.Select(async (index) => {
var response = await client.GetAsync("posts/" + index);
var contents = await response.Content.ReadAsStringAsync();
listResults.Add(contents);
Console.WriteLine(contents);
});
await Task.WhenAll(tasks);
Поскольку Select
ожидает Func<T, TResult>
, он будет интерпретировать async
лямбда без return
как async Task
метод вместо async void
, и, таким образом, дать нам то, что мы можем явно await