C # do some work after all events are raised (or combine events)

I want to subscribe to an event that will be raised when all several other events are raised.

Suppose I have several tasks ( A ) to do first (for example, animating several independent views), I can also subscribe to the event that the task is completed, and I would like to do some other work ( B ) after all these events.

The number of first tasks ( A ) may vary each time, so at the moment I set the counter for the number of tasks A , subscribe to N events and in the event handler to complete the task I decrease the counter, and when it is zero, I execute task B.

Is there a better way to combine these events than using a counter?

+4
source share
2 answers

If I understand the question correctly, you increase the counter when starting tasks A and when each task is completed, you decrease the counter in the event handler. Also in the event handler you check (after decreasing the counter) if the counter is zero. If so, you complete task B.

I suggest you look at Tasks (also called "Parallel Task Library (TPL)"), which allows you to do something like this:

 Task.WhenAll( new Task[] { Task.Run(()=> { //... do work A1... }, Task.Run(()=> { //... do work A2... }, Task.Run(()=> { //... do work A3... }}) .ContinueWith(()=> {//... do work B... }); 

Update: Based on the mention of the WPF animation in your comment below, Task.Run may NOT work. If I remember correctly, you get a Completed event and you cannot run the animation synchronously in the code (as in "... do work A1 ...").

However, instead of creating tasks using Task.Run you can create them from the Completed Storyboard event using the extension method, for example:

 public static Task<Storyboard> BeginAsync(this Storyboard sb) { var tcs = new TaskCompletionSource<Storyboard>(); sb.Completed += (s, a) => tcs.TrySetResult(sb); sb.Begin(); return tcs.Task; } 

Note that this method creates a task that is completed in the Completed storyboard event handler and starts the storyboard animation before returning the task. Also note that you can write a similar extension method for other types and events.

You should use this method, for example:

 var sb1 = (Storyboard)mainWindow.FindResource("Storyboard1"); var sb2 = (Storyboard)mainWindow.FindResource("Storyboard2"); var sb3 = (Storyboard)mainWindow.FindResource("Storyboard3"); Task.WhenAll( new Task[] { sb1.BeginAsync(), sb2.BeginAsync(), sb3.BeginAsync() }) .ContinueWith(() => MessageBox.Show("All done!"), TaskScheduler.FromCurrentSynchronizationContext()); 

TaskScheduler.FromCurrentSynchronizationContext() basically schedules the continuation task to run in the user interface thread (which is required if you will be accessing user interface elements).

+1
source

I would have an identifier (based on an enumeration, for example) for each event, and then add all the events that you expect to trigger in the list. Whenever an event is executed, I remove it from the list.

In each case, you call a method that will actually do any work when the list is empty.

0
source

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


All Articles