How to link independent C # tasks?

Let's say I have two independent asynchronous functions (which I do not control) that create Task objects:

Task A(); Task B(); 

and some other non-asynchronous function

 void X(); 

How to create a whole chain of tasks that performs all these sequences and allows you to add additional continuations (which will be executed after X)?

If I do this:

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

which does not work, because B will launch a new task chain. If B takes a long time, X will be executed first (along with what else the caller from SequentialWith can call on the returned Task). I need X to be the last element in the same task chain, task B as a precedent.

If I do this:

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

which only partially solves the problem, because although A, B and X will now be executed in sequential order, if the caller executes Sequential (). Continue. Continuation will be performed in parallel with B and X, and not after X.

+5
source share
3 answers

Is there any reason not to use? For instance,

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

Assuming you can't use async/await , as suggested in other answers (if you can, you should), there is a great way to extend for this scenario since Task in .NET 4.0: System.Threading.Tasks.TaskExtensions.Unwrap . It takes a Task<Task> (or Task<Task<TResult>> ) and "aligns" it into a continuous Task (or Task<TResult> respectively), which represents the completion of both an external task and an internal task.

Using this extension method, your method can be rewritten as:

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

The result of the Task will be the completion of the entire sequence of task chains in the expected order.

There is also the concept of "child tasks" , originally developed for this purpose in the early days of the parallel task library, but it is terribly difficult to use and requires that you have a lot of control over how tasks start, which you may not have. Nevertheless, it is worth knowing (if only for the sake of education).

+4
source

There is a pretty neat way to do this using the Microsoft 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(); } 

If we define methods, these are:

 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(); } 

Then execute Sequential().Wait(); causes the following:

  A
 A
 B
 B
 X
 X
+2
source

Source: https://habr.com/ru/post/1260746/


All Articles