我尝试通过以下项目的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;
}
答案 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()解决了该问题。
尽管如此,感谢您的回复!