目录
[2.1 同步事件执行](#2.1 同步事件执行)
[2.2 异步事件执行](#2.2 异步事件执行)
[2.3 等待异步事件完成](#2.3 等待异步事件完成)
[2.4 捕获异常处理中的异常](#2.4 捕获异常处理中的异常)
一,引言
都知道事件的本质是一个多播委托(MulticastDelegate),但对于事件的机制和用法一直懵懵懂懂,本篇主要对此进行深入分析,首先要明确关于事件的疑惑:
Event 是同步还是异步执行的?(答:同步执行)
如果是多个订阅,事件执行的顺序是什么?(答:串行执行)
如果事件执行中发生异常,会发生什么事情?(答:如果一个订阅者(事件)发生异常。未执行的事件不会继续执行)
事件支持异步执行吗?(答:支持)
事件触发后,跨进程可以触发到吗?(答:可以)
二,事件的定义和用法
事件作为类的成员,一般是通过事件向其他类或对象通知发生的相关事情。 发送事件的类称为发布者,接收事件的类称为订阅者。
发布者确定何时引发事件;订阅者确定对事件作出何种响应
一个事件可以有多个订阅者。 订阅者可以处理来自多个发行者的多个事件。
没有订阅者的事件永远也不会引发。
事件通常用于表示用户操作,例如单击按钮或图形用户界面中的菜单选项。
当事件具有多个订阅者时,引发该事件时会同步调用事件处理程序。 也可通过async/await达到异步调用事件的作用。
在 .NET 类库中,事件基于 EventHandler 委托和 EventArgs 基类。
2.1 同步事件执行
定义一个Demo类,其内部有个事件是 DemoEvent,我们给他开放了一个接口Raise,如果谁敢调用它,那么,它就触发报警事件DemoEvent。
            
            
              cs
              
              
            
          
                  public class Demo
        {
            public event EventHandler DemoEvent;
            public void Raise()
            {
                try
                {
                    this.DemoEvent?.Invoke(this, EventArgs.Empty);
                    Console.WriteLine("所有的事件处理已经被执行!");
                }
                catch (Exception ex)
                {
                }
            }
        }
        随后在主程序中对事件进行订阅(这里采用了匿名方法进行订阅):
            
            
              cs
              
              
            
          
           static void Main(string[] args)
        {
            var instance = new Demo();
            instance.DemoEvent += (sender, args) =>
            {
                Console.WriteLine("执行事件1!");
            };
            instance.DemoEvent += (sender, args) =>
            {
                Console.WriteLine("执行事件2!");
            };
            Console.WriteLine("*开始发起事件!");
            instance.Raise();
            Console.WriteLine("*事件执行完毕,继续下一项工作!");
            Console.ReadLine();
        }
        输出结果:

可以看到,事件是一次同步执行的(执行过程也会阻塞主线程)。
2.2 异步事件执行
在上面代码基础上,增加异步方法然后订阅:

结果输出:

可以看的,新增加的异步事件处理,的确是第一个被触发的,只不过它没有阻塞主线程处理。
小知识点:
- 在异步编程中虽然不推崇定义一个类似的
 async void xxxx(){}函数,因为这样的函数无法被主程序捕获结果或异常。 但凡是总有例外,而这个异步事件处理恰恰就是这个函数的最佳使用场景。- 上述代码是非UI编程,有关UI处理(按钮点击事件等),机制并不一样,UI为它的异步事件提供了一个SynchronizationContext,使它们能够在UI线程上恢复。它从不"等待"事件。
 
2.3 等待异步事件完成
虽然2.2完成了异步事件的执行,但是在上面的输出结果中,存在一个问题:
            
            
              cs
              
              
            
          
          *开始发起事件!
异步事件1执行开始
执行事件1!
执行事件2!
所有的事件处理已经被执行!
*事件执行完毕,继续下一项工作!
异步事件1执行完毕
        异步事件1执行完毕\]应该在\[\*事件执行完毕,继续下一项工作!\]前面输出才符合逻辑。但是异步执行的事件是不阻塞主线程的,那么**如何让主线程等待异步事件的完成呢**?
这就涉及到异步编程async/await内部机制的问题了,因此我们需要引入**SynchronizationContext**的内容,自定义一个继承类,来实现相关的操作:
```cs
        public class Demo
        {
            public event EventHandler DemoEvent;
            public void Raise()
            {
                try
                {
                    //3修改Raise函数,让事件的触发处在我们自定义的同步上下文内。
                    this.DemoEvent?.NaiveRaiseAsync(this, EventArgs.Empty).GetAwaiter().GetResult();
                    Console.WriteLine("所有的事件处理已经被执行!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("事件处理中发生异常!", ex.Message);
                }
            }
        }
        //主程序调用
        static void Main(string[] args)
        {
            var instance = new Demo();
            //采用匿名订阅异步事件
            instance.DemoEvent += async (sendr, args) =>
            {
                Console.WriteLine("异步事件1执行开始");
                await Task.Delay(10);
                Console.WriteLine("异步事件1执行结果");
            };
            //传统的订阅异步事件
            instance.DemoEvent += method2;
            instance.DemoEvent += (sender, args) =>
            {
                Console.WriteLine("执行事件1!");
            };
            instance.DemoEvent += (sender, args) =>
            {
                Console.WriteLine("执行事件2!");
            };
            Console.WriteLine("*开始发起事件!");
            instance.Raise();
            Console.WriteLine("*事件执行完毕,继续下一项工作!");
            Console.ReadLine();
        }
        
        //异步方法
        static async void method2(object sender, EventArgs e)
        {
            Console.WriteLine("异步事件2执行开始");
            await Task.Delay(100);
            Console.WriteLine("异步事件2执行完毕");
        }
        //1实现同步上下文(对异步的分裂点进行标记)
        public class NaiveSynchronizationContext:SynchronizationContext
        {
            private readonly Action completed;
            public NaiveSynchronizationContext(Action completed)
            {
                this.completed = completed;
            }
            public override SynchronizationContext CreateCopy()
            {
                return new NaiveSynchronizationContext(this.completed);
            }
            public override void OperationStarted()
            {
                Console.WriteLine("同步上下文: 开始");
            }
            public override void OperationCompleted()
            {
                Console.WriteLine("同步上下文: 完成");
                this.completed();
            }
        }
    }
    //2对NaiveExtension函数进行扩展
    public static class NaiveExtension
    {
        public static Task NaiveRaiseAsync(this EventHandler @this, object sender, EventArgs eventArgs)
        {
            // 如果没有事件处理,那么立即结束
            if (@this == null)
            {
                return Task.CompletedTask;
            }
            var delegates = @this.GetInvocationList();
            var count = delegates.Length;
            var tcs = new TaskCompletionSource