Почему этот метод ParallelForEachAsync никогда не возвращается?
Я пытаюсь выполнить этот код асинхронно и параллельно, используя метод ParallelForEachAsync из этого проекта: https://github.com/Dasync/AsyncEnumerable. К сожалению, метод никогда не возвращается. SampleProduct - это простой DTO, который имеет логическое свойство и два строковых свойства. Метод GetOsmData пытается получить данные через http-запрос, но часто выдает исключение.
Моя первая попытка была без.ConfigureAwait(false), но имеет тот же результат... Если я попробую этот метод со списком продуктов (products.Count = 8), то получится. Количество всегда останавливается на 7.
private async Task<ConcurrentBag<SampleProduct>> CheckOsmDataAsync(List<SampleProduct> products)
{
var result = new ConcurrentBag<SampleProduct>();
await products.ParallelForEachAsync(
async product =>
{
OsmData osmData;
try
{
osmData = await GetOsmData(_osmUrl.Replace("articlenumber", product.MaterialNumber.ToString())).ConfigureAwait(false);
}
catch (Exception e)
{
osmData = null;
}
if (osmData != null && osmData.PrintingData.Count > 0)
{
product.OsmPrintImageAvailable = true;
}
else
{
product.OsmPrintImageAvailable = false;
}
result.Add(product);
},
// 0 => Chooses a default value based on the processor count
maxDegreeOfParallelism: 0
);
return result;
}
2 ответа
С помощью моего коллеги я смог решить проблему... Проблема была не в самом методе, а только в том, как он был вызван. Я назвал это из Main/UI-Thread синхронно. Это, кажется, вызвало тупик. Выполнение асинхронного вызова метода и ожидание CheckOsmDataAsync() решило проблему.
Тем не менее, спасибо за ваши ответы!
Возможно ли, что метод GetOsmData
никогда не возвращается при некоторых условиях? Чтобы исключить такую возможность, вы можете принудительно отключить ее по истечении разумного периода времени. Вы можете использовать метод расширения ниже для достижения этого:
public static Task<T> TimeoutAfter<T>(this Task<T> task, int timeout)
{
var delayTask = Task.Delay(timeout).ContinueWith<T>(_ => throw new TimeoutException(),
TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, delayTask).Unwrap();
}
Это можно использовать так:
osmData = await GetOsmData(_osmUrl.Replace("articlenumber",
product.MaterialNumber.ToString())).TimeoutAfter(5000).ConfigureAwait(false);
Это не навсегда, потому что мы не знаем, что GetOsmData
Задача будет делать дальше, так как теперь она стала жуликом. Плохой сценарий заключается в том, что он будет постоянно удерживать поток пула потоков, и рано или поздно пул потоков будет исчерпан из-за слишком большого количества мошеннических задач. Надеюсь, это не та проблема, с которой вы сталкиваетесь, потому что, если это так, ее не легко решить.