c# await (2) of them - c#

I have 2 awaits that I run to get data from an external service:
aaa= await Gateway.GetMyAAA();
bbb= await Gateway.GetBBBB();
I want both to run at the same time and then when both are done, to continue.
How do I Wait for them all ?

var results = await Task.WhenAll(task1, task2);
results is the array of your results and
results[0] is the result of your task1

Don't use await until you get the reference to all the tasks. Then await them all at once.
Task aaa = Gateway.GetMyAAA();
Task bbb = Gateway.GetBBBB();
await Task.WhenAll(aaa, bbb);
'all tasks are complete at this time. now we get the results
var aaaResult = await aaa;
var bbbResult = await bbb;

First, you need to save the Task instances, rather than awaiting them directly:
Task aaaTask = Gateway.GetMyAAA(), bbbTask = Gateway.GetMyBBBB();
Then you wait both (all) of them in a single call to WhenAll():
await Task.WhenAll(aaaTask, bbbTask);
Finally, you retrieve the results:
aaa = await aaaTask;
bbb = await bbbTask;
Note that the final await statements will complete synchronously. The await statement mainly is there so that the compiler will automatically unpack any exceptions that might have occurred and cause them to be thrown "normally", instead of in an aggregated exception.

Related

Parallel execution with return values and async/await

What is the correct way to handle a part of code that you want to execute at the same time in C#? Each of these calls takes about 3 seconds each (we are making indexing improvements in our systems currently). How do I make these statements execute in parallel with results?
var properties = await _propertyService.GetPropertiesAsync("Fairfax, VA");
var ratings = await _ratingsService.GetRatingsAsync(12549);
You can remove await from invocation and await for result of Task.WhenAll:
var propertiesTask = _propertyService.GetPropertiesAsync("Fairfax, VA");
var ratingsTask = _ratingsService.GetRatingsAsync(12549);
await Task.WhenAll(propertiesTask, ratingsTask);
var properties = propertiesTask.Result;
var ratings = ratingsTask.Result;
Or split method invocation and awaiting (which is usually less preferable option):
var propertiesTask = _propertyService.GetPropertiesAsync("Fairfax, VA");
var ratingsTask = _ratingsService.GetRatingsAsync(12549);
var properties = await propertiesTask;
var ratings = await ratingsTask;
You can use Task.WhenAll for that.
Task.WhenAll will not block and can be awaited, yielding control back to the caller until all tasks finish (in contrast to Task.WaitAll)
In terms of exceptions, If any of the prvided tasks completes in a faulted state, then the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of the unwrapped exceptions from each of the supplied tasks.
var task1 = _propertyService.GetPropertiesAsync("Fairfax, VA");
var task2 = _ratingsService.GetRatingsAsync(12549);
await Task.WhenAll(task1, task2);

Awaiting tasks of different types

IEnumerable<Task<Request>> requestTasks = CreateRequestTasks();
Task<Trace> traceTask = CreateTraceTask();
var tasks = new List<Task>();
tasks.AddRange(requestTasks);
tasks.Add(traceTask);
await Task.WhenAll(tasks);
How do I get the result from the requestTasks collection?
How do I get the result from the requestTasks collection?
Keep it as a separate (reified) collection:
List<Task<Request>> requestTasks = CreateRequestTasks().ToList();
...
await Task.WhenAll(tasks);
var results = await Task.WhenAll(requestTasks);
Note that the second await Task.WhenAll won't actually do any "asynchronous waiting", because all those tasks are already completed.
Since you have to await them all, you can simply write
IEnumerable<Task<Request>> requestTasks = CreateRequestTasks();
Task<Trace> traceTask = CreateTraceTask();
var tasks = await Task.WhenAll(requestTasks);
var trace = await traceTask;
inside an equivalent async block: it may look clearer imho.
Notice also that the above traceTask starts on create (actually this is the same answer since the question itself is a duplicate).

Using async in non-async method

Lets say I only want one method to run in async.
So I have an async method like below:
public async Task Load(){
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
And then I'm trying to call that async method in another method as a task, and would like for it to wait until that particular piece of async code is done. The problem is it's not. When it reaches the first await in Load() it just doesn't finish loading. The debugger goes blank and gives no other error.
Is an async method able to be called from a non async method, like this?
There is a reason I do not need this particular task to be async, but the Load() function I do.
public void GetSomethingElse(){
var task1 = Load().Wait();
}
How is this possible?
I tried even changing the Load() method to use var data = task1.Wait(), etc. instead of await, still no difference, no matter which way I try. If anyone can help it would be appreciated.
You probably have a deadlock on your hands. You're blocking a thread using Wait() on a task that needs that thread to complete because there's a SynchronizationContext being used in ASP.Net (also in GUI environments).
You should use ConfigureAwait(false) to tell the awaiter not to capture that context. It's enough to do that on the first await since the rest would have no SynchronizationContext to capture:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2;
var data3 = await task3;
//..process data.
}
However, it's recommended to always use ConfigureAwait unless you want to capture the SynchronizationContext so a better standard is this:
public async Task Load()
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1.ConfigureAwait(false);
var data2 = await task2.ConfigureAwait(false);
var data3 = await task3.ConfigureAwait(false);
//..process data.
}
In your case, where you want to continue after all the tasks completed you should use Task.WhenAll instead of awaiting each task separately:
public async Task Load()
{
await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false);
// process data.
}
Note: doing sync over async is usually discouraged as it has no benefits (you're blocking a thread throughout the entire operation) and may cause deadlocks (like this one).
I think you have a classic deadlock scenario. See this post for more details. When you have an await statement the current SynchronizationContext is stored before the await call and restored afterwards and the rest of the method posted to it. In GUI app there is only one thread associated with that context so the rest of the method is attemted to be executed on the GUI thread but it can't
because Wait() is a blocking call which blocks the GUI thread.
Try this instead:
public async Task Load(){
Task task1 = GetAsync(1).ConfigureAwait(false);
Task task2 = GetAsync(2).ConfigureAwait(false);
Task task3 = GetAsync(3).ConfigureAwait(false);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
}
If there are any awaits inside GetAsync you may have to add .ConfigureAwait(false) there as well.
You would change your Load function as following:
public async Task Load(){
await new TaskFactory().StartNew(() =>
{
Task task1 = GetAsync(1);
Task task2 = GetAsync(2);
Task task3 = GetAsync(3);
var data1 = await task1; // <--Freezes here when called from GetSomethingElse()
var data2 = await task2;
var data3 = await task3;
..process data..
});
}

Task.WhenAll in PCL

I'm trying to run a few async tasks concurrently inside my Portable Class Library, but the WhenAll method doesn't appear to be supported.
My current workaround is to start each task and then await each of them:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var result1 = await task1;
var result1 = await task2;
var result1 = await task3;
Is there something I'm missing? Do I need to make do with the workaround?
Is there something I'm missing?
Yes: Microsoft.Bcl.Async.
Once you install that package, then you can use TaskEx.WhenAll as a substitute for Task.WhenAll:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var results = await TaskEx.WhenAll(task1, task2, task3);
P.S. Consider using the term parallel for (CPU-bound) parallel processing, and the term concurrent for doing more than one thing at a time. In this case, the (I/O-bound) tasks are concurrent but not parallel.

Concurrent execution of async methods

Using the async/await model, I have a method which makes 3 different calls to a web service and then returns the union of the results.
var result1 = await myService.GetData(source1);
var result2 = await myService.GetData(source2);
var result3 = await myService.GetData(source3);
allResults = Union(result1, result2, result3);
Using typical await, these 3 calls will execute synchronously wrt each other. How would I go about letting them execute concurrently and join the results as they complete?
How would I go about letting them execute in parallel and join the results as they complete?
The simplest approach is just to create all the tasks and then await them:
var task1 = myService.GetData(source1);
var task2 = myService.GetData(source2);
var task3 = myService.GetData(source3);
// Now everything's started, we can await them
var result1 = await task1;
var result1 = await task2;
var result1 = await task3;
You might also consider Task.WhenAll. You need to consider the possibility that more than one task will fail... with the above code you wouldn't observe the failure of task3 for example, if task2 fails - because your async method will propagate the exception from task2 before you await task3.
I'm not suggesting a particular strategy here, because it will depend on your exact scenario. You may only care about success/failure and logging one cause of failure, in which case the above code is fine. Otherwise, you could potentially attach continuations to the original tasks to log all exceptions, for example.
You could use the Parallel class:
Parallel.Invoke(
() => result1 = myService.GetData(source1),
() => result2 = myService.GetData(source2),
() => result3 = myService.GetData(source3)
);
For more information visit: http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel(v=vs.110).aspx
As a more generic solution you can use the api I wrote below, it also allows you to define a real time throttling mechanism of max number of concurrent async requests.
The inputEnumerable will be the enumerable of your source and asyncProcessor is your async delegate (myservice.GetData in your example).
If the asyncProcessor - myservice.GetData - returns void or just a Task without any type, then you can simply update the api to reflect that. (just replace all Task<> references to Task)
public static async Task<TOut[]> ForEachAsync<TIn, TOut>(
IEnumerable<TIn> inputEnumerable,
Func<TIn, Task<TOut>> asyncProcessor,
int? maxDegreeOfParallelism = null)
{
IEnumerable<Task<TOut>> tasks;
if (maxDegreeOfParallelism != null)
{
SemaphoreSlim throttler = new SemaphoreSlim(maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value);
tasks = inputEnumerable.Select(
async input =>
{
await throttler.WaitAsync();
try
{
return await asyncProcessor(input).ConfigureAwait(false);
}
finally
{
throttler.Release();
}
});
}
else
{
tasks = inputEnumerable.Select(asyncProcessor);
}
await Task.WhenAll(tasks);
}

Categories

Resources