DougJones
DougJones

Reputation: 782

Start async operations, then await later

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

Answers (2)

DougJones
DougJones

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

Stephen Cleary
Stephen Cleary

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

Related Questions