为什么此ParallelForEachAsync-Method永不返回?

时间:2019-04-29 14:35:49

标签: c# parallel-processing parallel.foreach iasyncenumerable

我尝试通过以下项目的https://github.com/Dasync/AsyncEnumerable使用ParallelForEachAsync方法来异步和并行执行此代码。不幸的是,该方法永远不会返回。 SampleProduct是一个简单的DTO,具有布尔属性和两个字符串属性。 GetOsmData方法尝试通过http-request获取数据,但通常会引发异常。

我的第一次尝试是没有.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 个答案:

答案 0 :(得分:0)

在某些情况下,方法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下一步将做什么,因为现在它已变成rogue。糟糕的情况是,它将使线程池线程永远被占用,并且由于过多的流氓任务,线程池迟早会耗尽。希望这不是您要处理的问题,因为如果是这样,这不是一个容易解决的问题。

答案 1 :(得分:0)

在同事的帮助下,我可以解决问题...问题不在于方法本身内部,而仅在于调用方法的方式。我称它为主/ UI线程同步。这似乎造成了僵局。使调用方法异步并等待CheckOsmDataAsync()解决了该问题。

尽管如此,感谢您的回复!