简体   繁体   English

在引发所有事件(或组合事件)后,C#做一些工作

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

I'd like to subscribe to an event that will be raised when all of multiple other events are raised. 我想订阅在引发所有其他多个事件时将引发的事件。

Suppose I have multiple tasks ( A ) to do first (for example animating multiple independent views), I can also subscribe to the event that the task is complete, and I'd like to do some other work ( B ) after all of these events are finished. 假设我有多个任务( A )要先做(例如动画多个独立视图),我也可以订阅任务完成的事件,并且我想在所有这些之后做一些其他工作( B )事件结束了。

The amount of first tasks ( A ) can differ each time, so at the moment I set a counter to number of tasks A , subscribe to N events, and in the event handler for the task completion I decrement the counter, and when it's zero, I do the task B. 第一个任务( A )的数量每次都可以不同,所以目前我设置一个计数器到任务数A ,订阅N个事件,并在任务完成的事件处理程序中减少计数器,当它为零时,我做任务B.

Is there a nicer way to combine these events than by using a counter? 结合这些事件比使用计数器有更好的方法吗?

If I understand the question correctly, you increment a counter when you start your A tasks and when each task is completed, you decrement the counter in the event handler. 如果我正确理解了这个问题,那么当你启动A任务时增加一个计数器,当每个任务完成时,你会减少事件处理程序中的计数器。 Also in the event handler, you check to see (after decrementing the counter) if the counter is zero. 同样在事件处理程序中,如果计数器为零,则检查(在递减计数器之后)。 If so, you do task B. 如果是这样,你做任务B.

I suggest you look at Tasks ( aka "Task Parallel Library (TPL)" ), which allows you to do something like this: 我建议你看看Tasks又名“任务并行库(TPL)” ),它允许你做这样的事情:

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 WPF animations in your comment below, Task.Run may NOT be a good fit here. 更新:根据您在下面的评论中提到的WPF动画, Task.Run可能不适合这里。 If I remember correctly, you get a Completed event, and don't have a way to run animations synchronously in code (as in "...do work A1..."). 如果我没记错的话,你会得到一个Completed事件,并且没有办法在代码中同步运行动画(如“......做工作A1 ......”)。

However, instead of creating tasks via Task.Run , you can create them from the Completed event of a Storyboard via an extension method like: 但是,您可以通过以下扩展方法从StoryboardCompleted事件创建任务,而不是通过Task.Run创建任务:

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 which is completed in the storyboard's Completed event handler, and begins the storyboard animation before returning the task. 请注意,此方法创建一个在故事板的Completed事件处理程序中完成的任务,并在返回任务之前开始故事板动画。 Also note that you can write a similar extension method for other types and events. 另请注意,您可以为其他类型和事件编写类似的扩展方法。

You'd use this method like, 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 on the UI thread (which is required if you will be accessing the UI elements). TaskScheduler.FromCurrentSynchronizationContext()基本上调度继续任务以在UI线程上运行(如果您将访问UI元素,则需要这样做)。

I would have an identifier (based on an enum, for example) for each event and then add all the events you expect to be called to the list. 我将为每个事件设置一个标识符(例如,基于枚举),然后将您希望调用的所有事件添加到列表中。 Whenever the event is executed, I would remove it from the list. 每当事件执行时,我都会从列表中删除它。

In each event you call a method that will only actually do any work when the list is empty. 在每个事件中,您调用的方法只有在列表为空时才会实际执行任何操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM