简体   繁体   中英

Combine Results from Async Method and Return Synchronously

I've read many posts where people have faced similar issues but they seem to make assumptions that don't apply or their code simply does not work for me. I need to combine results from asynchronous methods. The ONLY thing async that I want is the combining of the results. Since the Azure Service Bus will only allow me to grab 256 messages at a time, I want to send off multiple requests to get a few batches at once and make it into one list.

There seems to be an assumption that if you are calling an async method you want to return to the caller while the work completed (ie: some long running task). However, I don't want that at all. I want to wait on the tasks to complete and then take my combined list and return it.

Firstly I don't want to mark my calling method with async. I can but why should I, I am by all means calling an synchronous method that happens to be performing some asynchronous actions before getting back to me.

I have seen examples using WhenAll() and then working with the Result, but this doesn't work for me. I've tried all different permutations but it either locks up my app or it tells me the task hasn't been executed yet.

Here is what I have currently:

public IEnumerable<BrokeredMessage>[] GetCombinedResults()
{
            var job = () => ServiceBus.TrackerClient.ReceiveBatchAsync(BatchLimit);
            Task<IEnumerable<BrokeredMessage>> task1 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job);
            Task<IEnumerable<BrokeredMessage>> task2 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job);
            IEnumerable<BrokeredMessage>[] results = Task.WhenAll<IEnumerable<BrokeredMessage>>(task1, task2).Result;
            return results;
}

But calling results causes it to lock up. I've read deadlocks can occur doing this but have seen this proposed as an answer in other questions. If I call Task.WaitAll() and care nothing on the results this type of setup works fine. Not sure why this becomes difficult when I want the returned results from the tasks. I've tried using Task.Run but then it exits my method before ever getting the results.

You seem to be interested in using async for fan-out parallelism. That's a totally valid thing to do. It is not necessary to make the entire call chain async to make use of fan-out parallelism.

You stumbled over the usual ASP.NET deadlock. You can use Task.Run as a simple, fail-safe way to avoid it. First, let's make GetCombinedResults async to keep it simple and consistent:

public async Task<IEnumerable<BrokeredMessage>[]> GetCombinedResultsAsync()
{
            var job = () => ServiceBus.TrackerClient.ReceiveBatchAsync(BatchLimit);
            Task<IEnumerable<BrokeredMessage>> task1 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job);
            Task<IEnumerable<BrokeredMessage>> task2 = _retryPolicy.ExecuteAsync<IEnumerable<BrokeredMessage>>(job);
            IEnumerable<BrokeredMessage>[] results = await Task.WhenAll<IEnumerable<BrokeredMessage>>(task1, task2);
            return results;
}

This method is clearly correct. It does not mix sync and async. Call it like that:

var results = Task.Run(() => GetCombinedResultsAsync()).Result;

The reason this works is that GetCombinedResultsAsync executes without synchronization context now.

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