等待一些功能并在等待Task.WhenAll时继续运行一些代码。

时间:2019-03-08 07:08:54

标签: c# async-await task

有以下功能。

async Task<int> T1() { Console.WriteLine("T1"); return await Task.FromResult(1); }
async Task<string> T2() { Console.WriteLine("T2"); return await Task.FromResult("T2"); }
async Task<char> T3() { await Task.Delay(2000); Console.WriteLine("T3"); return await Task.FromResult('A'); }
async Task<string> T4() { Console.WriteLine("T4"); return await Task.FromResult("T4"); }

// U1, U2, U3, and U4 need to be run right after T1, T2, T3, and T4 respectively
void U1() { System.Threading.Thread.Sleep(1000); Console.WriteLine($"After T1"); }
void U2() { System.Threading.Thread.Sleep(4000); Console.WriteLine($"After T2"); }
void U3() { System.Threading.Thread.Sleep(1000); Console.WriteLine($"After T3"); }
void U4() { System.Threading.Thread.Sleep(1000); Console.WriteLine($"After T4"); }

// TAll() needs to be run as soon as T1, T2, T3, and T4 finished.
void TAll() { Console.WriteLine("To be run after T1, T2, T3, T4"); }

// All() runs after all functions are done.
void All() { Console.WriteLine("To be run after U1, U2, U3, U4"); }

但是,以下呼叫

var t1 = T1().ContinueWith(_ => U1());
var t2 = T2().ContinueWith(_ => U2());
var t3 = T3().ContinueWith(_ => U3());
var t4 = T4().ContinueWith(_ => U4());
await Task.WhenAll(t1, t2, t3, t4);
TAll();

All();

返回

T1
T2
T4
After T1
After T4
T3
After T3
After T2
To be run after T1, T2, T3, T4
To be run after U1, U2, U3, U4

预期输出顺序为

T1
T2
T4
After T1
After T4
T3
To be run after T1, T2, T3, T4
After T3
After T2
To be run after U1, U2, U3, U4

3 个答案:

答案 0 :(得分:4)

您应该use async and await rather than ContinueWith。就您而言,添加新的async方法将简化代码:

var t1 = T1();
var u1 = InvokeU1(t1);
var t2 = T2();
var u2 = InvokeU2(t2);
var t3 = T3();
var u3 = InvokeU3(t3);
var t4 = T4();
var u4 = InvokeU4(t4);

await Task.WhenAll(t1, t2, t3, t4);
TAll();

await Task.WhenAll(u1, u2, u3, u4);
All();

async Task InvokeU1(Task task) { await task; U1(); }
async Task InvokeU2(Task task) { await task; U2(); }
async Task InvokeU3(Task task) { await task; U3(); }
async Task InvokeU4(Task task) { await task; U4(); }

答案 1 :(得分:3)

任务的继续实际上是任务。在您的示例中,您正在等待继续,因此在完成所有目标任务及其所有继续时,将记录“要在...之后运行”

考虑一下:

//target tasks
var t1 = T1();
var t2 = T2();
var t3 = T3();
var t4 = T4();

//continuations
var c1 = t1.ContinueWith(_ => U1());
var c2 = t2.ContinueWith(_ => U2());
var c3 = t3.ContinueWith(_ => U3());
var c4 = t4.ContinueWith(_ => U4());

await Task.WhenAll(t1, t2, t3, t4);
TAll();

await Task.WhenAll(c1, c2, c3, c4);
All();

输出将符合您的期望。

更新

Stephen添加了一个有关ContinueWith的很好的技巧,我鼓励您使用它。但是,无论ContinueWith多么危险,我都试图从概念上解释问题。

答案 2 :(得分:0)

由于您拥有await Task.WhenAll(t1, t2, t3, t4);,因此可确保T1(),T2(),T3(),T4()及其关联的U1(),U2(),U3(),U4()(顺序取决于在每个线程中提到的sleep或task.deley线程)在继续执行序列中的TALL()和ALL()之前完成。因此,下面的两个语句是最后要打印的

To be run after T1, T2, T3, T4
To be run after U1, U2, U3, U4