2016-12-03 4 views
5

比方說,我有兩個獨立的異步功能(我不控制),其創建任務對象:如何鏈接獨立的C#任務?

Task A(); 
Task B(); 

和其他一些非異步功能

void X(); 

如何構建一個單一任務鏈按順序執行所有這些任務鏈,並允許進一步延續(將在X之後執行)以被追加?

如果我這樣做:

Task Sequential() 
{ 
    return A() 
    .ContinueWith(t => { B(); }) 
    .ContinueWith(t => { X(); }); 
} 

不起作用,因爲B就開始了新的任務鏈。如果B需要很長時間才能完成,則將首先執行X(以及Sequential的調用者可能在返回的任務中繼續執行的任何其他操作)。我需要X是單個任務鏈中的最後一個元素,以B任務爲先例。

如果我這樣做:。

Task Sequential() 
{ 
    return A() 
    .ContinueWith(t => { B().ContinueWith(t => { X(); }); }); 
} 

,僅部分地解決了這個問題,因爲即使A,B和X將現在按順序執行,如果主叫方不連續()ContinueWith,即延續將並行執行B和X,而不是X.

+0

Alex的答案是,你會想要去的方式。但是出於你的目的,你試圖完成的事情需要使用承諾(TaskCompletionSource '服務)。 –

+1

如果你說'A'和'B'是獨立的,那麼是不是'Task.WaitAll(A(),B())。ContinueWith(t => X())' – VMAtm

+0

@VMAtm,你的意思是'Task.WhenAll'。 –

回答

7

是否有任何理由不使用await?例如,

async Task Sequential() 
{ 
    await A(); 
    await B(); 
    X(); 
} 
+0

謝謝,這工作! –

4

假設你不能使用async/await在其他的答案建議(如果可以的話,你應該),有可用,因爲在.NET推出Task以應付這種情況下一個可愛的小擴展方法4.0:System.Threading.Tasks.TaskExtensions.Unwrap。它需要一個Task<Task>(或Task<Task<TResult>>),並將其「扁平」爲連續的Task(或分別爲Task<TResult>),表示完成外部任務和內部任務。

使用擴展方法,你的方法可以被改寫爲:

Task Sequential() 
{ 
    return A() 
     .ContinueWith(t => B()).Unwrap() 
     .ContinueWith(t => X()); // You said X() is "non-async", so no Unwrap here. 
} 

所得Task將代表任務的整個順序鏈的完成,按預期的順序。

還有the concept of "child tasks"最初設計用於在任務並行庫初期這個目的,但它的窘況難以使用,需要你有過怎樣的任務開始,你可能極大控制沒有。不過,值得了解(如果只是爲了教育)。

2

使用微軟的Reactive Framework(NuGet「System.Reactive」)來做這件事相當簡單。

public Task Sequential() 
{ 
    return 
    (
     from a in Observable.FromAsync(() => A()) 
     from b in Observable.FromAsync(() => B()) 
     from c in Observable.Start(() => X()) 
     select c 
    ).ToTask(); 
} 

如果我們定義的方法是這樣的:

public Task A() { return Task.Run(() => { "A".Dump(); Thread.Sleep(1000); "A".Dump(); }); } 
public Task B() { return Task.Run(() => { "B".Dump(); Thread.Sleep(1000); "B".Dump(); }); } 
public void X() { "X".Dump(); Thread.Sleep(1000); "X".Dump(); } 

然後運行Sequential().Wait();產生這樣的:

 
A 
A 
B 
B 
X 
X