Reputation: 782
I'm just now starting to play with async and await. I have 8 separate db calls, some dependent on others. I'd like to be able to kick off 3 async methods, then when a certain 1 returns, kick off 3 others, then when a certain 1 returns from that, kick off 2 more. I am currently using 3 Parallel.Invoke methods to accomplish this, but each parallel has to wait until ALL methods return. I only care about 1 method returning, the others can run in the background until an await Task.WhenAll(t1,t2,t3,...,t6)
at the end. Is there a way to pull this off with async/await?
I understand that await isn't blocking, but it is stopping execution of my main method (w/the 8 db calls) until the value returns from the method (just like a synchronous method does).
Upvotes: 10
Views: 5562
Reputation: 782
My issue was that I didn't know about Task.Run
that allows you to kick off a task, then await it later. I found this in another answer by @StephenCleary. Thanks Stephen, you've got my upvote there! Here's my completely contrived example that accomplishes what I set out to do above:
Func<Task<int>> t1 = async () => { await Task.Delay(4000); return 1; };
Func<Task<int>> t2 = async () => { await Task.Delay(5000); return 2; };
Func<int, Task<int>> t3 = async (val) => { await Task.Delay(3000); return 3; };
Func<int, Task<int>> t4 = async (val) => { await Task.Delay(4000); return 4; };
Func<int, Task<int>> t5 = async (val) => { await Task.Delay(3000); return 5; };
Func<int, Task<int>> t6 = async (val) => { await Task.Delay(2000); return 6; };
Func<int, Task<int>> tSecondary = async (val) => { await Task.Delay(3000); return 7; };
Func<Task<int>> tPrimary = async () => { await Task.Delay(2000); return 8; };
//kick off first 3 calls
var primaryR = Task.Run(tPrimary);
var t1R = Task.Run(t1);
var t2R = Task.Run(t2);
//await 1 of the 3
var primary = await primaryR;
//start 2 of 3 dependent tasks
var t3R = Task.Run(() => t3(primary));
var t4R = Task.Run(() => t4(primary));
//kick off and await secondaryMasterTaskAsync
var secondary = await tSecondary(primary);
//start final 2
var t5R = Task.Run(() => t5(secondary));
var t6R = Task.Run(() => t6(secondary));
//await all tasks that haven't been awaited previously
var tasks = await Task.WhenAll(t1R, t2R, t3R, t4R, t5R, t6R);
Upvotes: 0
Reputation: 456322
You can use Task.WhenAny
to wait for any one of several tasks:
var completedTask = await Task.WhenAny(t1, t2, t3, ...);
If you have a more complex dependency structure, then I recommend representing that with async
methods as such:
static async Task DoMasterOperationAsync()
{
var result = await StartSomething();
await Task.WhenAll(DoComplexOperationAsync(), result.NextT1Async());
}
static async Task DoComplexOperationAsync()
{
var result1 = await T1Async();
await Task.WhenAll(result1.NextT1Async(), result1.NextT2Async(), result1.NextT3Async());
}
await Task.WhenAll(DoMasterOperationAsync(), t2, t3, ...);
Upvotes: 5