简体   繁体   中英

Why does this ParallelForEachAsync-Method never return?

I try to execute this code async and parallel by using the ParallelForEachAsync method from this project: https://github.com/Dasync/AsyncEnumerable . Unfortunately the method never returns. The SampleProduct is a simple DTO which has a boolean property and two string properties. The GetOsmData-method tries to get data via http-request but often throws an exception.

My first attempt was without .ConfigureAwait(false), but has the same result... If I try this method with a list of products (products.Count = 8) result.Count always stopps by 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;
        }

Is it possible that the method GetOsmData never returns under some conditions? To rule out this possibility you could force it to timeout after a reasonable duration. You can use the extension method bellow to achieve it:

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();
}

It can be used like this:

osmData = await GetOsmData(_osmUrl.Replace("articlenumber",
    product.MaterialNumber.ToString())).TimeoutAfter(5000).ConfigureAwait(false);

This is not a permanent fix because we don't know what the GetOsmData task will do next, since now it has become rogue . A bad scenario is that it will keep a thread-pool thread occupied forever, and sooner or later the thread-pool will be exhausted because of too many rogue tasks. Hopefully this is not the problem you are dealing with, because if it is, it's not an easy problem to solve.

With the help of my colleague I could solve the problem... The problem was not inside the method itself, but only how it was called. I called it form the Main/UI-Thread synchronously. That seems to have caused a deadlock. Making the calling method async and awaiting the CheckOsmDataAsync() solved the problem.

Nevertheless thanks for your responds!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM