您当前的位置: 首页 >  c#

Peter_Gao_

暂无认证

  • 1浏览

    0关注

    621博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C# 异步事件调用委托

Peter_Gao_ 发布时间:2020-04-27 07:52:44 ,浏览量:1

异步 需要用的地方挺多,有必要总结一下。 msdn文档:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/ 官方的简介: *.NET Framework提供了执行异步操作的三种模式:异步编程模型(APM)模式(也称为IAsyncResult的模式),其中异步操作要求Begin和End方法(例如,BeginWrite和EndWrite异步写入操作)。这种模式不再被推荐用于新开发。有关更多信息,请参阅异步编程模型(APM)。

基于事件的异步模式(EAP),它需要一个具有Async后缀的方法,并且还需要一个或多个事件,事件处理程序委托类型和被EventArg派生类型。EAP在.NET Framework 2.0中引入。不再推荐新的开发。有关更多信息,请参阅基于事件的异步模式(EAP)。

基于任务的异步模式(TAP),它使用单一方法来表示异步操作的启动和完成。TAP在.NET Framework 4中引入,是.NET Framework中推荐的异步编程方法。C#中的async和wait等待关键字,Visual Basic语言中的Async和Await运算符为TAP添加语言支持。有关更多信息,请参阅基于任务的异步模式(TAP)。* Task-based Asynchronous Pattern (TAP), which uses a single method to represent the initiation and completion of an asynchronous operation. TAP was introduced in the .NET Framework 4. It's the recommended approach to asynchronous programming in .NET. 

 

方法一(不再被推荐用):使用回调方法完成异步委托

首先定义一个string类型的返回值、string类型的参数的委托

class Program     {         delegate string SayHi(string name);//定义委托         static void Main(string[] args)         {             SayHi sayhi = new SayHi(SayHiName); //实例化委托             sayhi("科比"); //一般的直接同步调用             sayhi.Invoke("张林"); //使用Invoke方法同步调用

            //异步调用             sayhi.BeginInvoke("杜兰特", (IAsyncResult  ar) =>             {

               //必须用EndInvoke()获取异步调用的结果                sayhi.EndInvoke(ar);                 Console.WriteLine("打招呼成功结束");              }, null);         }         public static string SayHiName(string  name)         {             return "how are you"+name + "?";         }     }  

前两种调用委托的方式都是同步的,BeginInvoke方法的返回值是IAsyncResult类型的 该方法的参数由两部分组成,前面(n)个参数是委托的参数,倒数第二个参数也表示一个委托,该委托是.net系统定义的委托(和func、action类似),查看AsyncCallback的定义如图:

作用就是:作为执行调用的回调方法,值得注意的是,在回调方法中,必须调用EndInvoke方法结束异步调用,EndInvoke是获取异步调用的结果 上面的例子调试的结果如图: 

 

 

 

如何实现异步事件调用呢?事件其实是一种MulticastDelegate(多播委托)。而MulticastDelegate类提供了一个GetInvocationList方法,该方法返回此多播委托的委托调用数组。利用该方法就能实现我们的异步事件调用功能。

 

using System;

using System.Threading;

using System.Runtime.Remoting.Messaging;

namespace ProcessTest

{

    class Program

    {

        //定义一个事件

        public static event EventHandler OnEvent;

 

        // 订阅者 方法1

        static void Method1(object sender, EventArgs e)

        {

            //显示执行该方法的线程ID

            Console.WriteLine("调用Method1的线程ID为:{0}", Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(1000);

        }

        // 订阅者 方法2

        static void Method2(object sender, EventArgs e)

        {

            Console.WriteLine("调用Method2的线程ID为:{0}", Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(1000);

        }

        static void Main(string[] args)

        {

            //显示主线程ID

            System.Console.WriteLine("主线程ID为:{0}", Thread.CurrentThread.ManagedThreadId);

 

            // 事件订阅

            //将Method1和Method2注册到事件中

            OnEvent += new EventHandler(Method1);

            OnEvent += new EventHandler(Method2);

 

             // 发布者

            //下面的代码实现事件的异步调用

            //获取事件中的多路委托列表

            Delegate[] delegAry = OnEvent.GetInvocationList();

            //遍历委托列表

            foreach (EventHandler deleg in delegAry)

            {

                //异步调用委托

第一个参数为要回调的函数(执行完自身的方法后会继续执行的方法),第二个参数为要向回调函数传入的值(自身的委托,方便在回调函数中获取执行完返回的信息)

                deleg.BeginInvoke(null, EventArgs.Empty, null, null);

            }

            System.Console.ReadKey();

        }

    }

}

 

 Demo: 

三个页面:Observer.cs(观察者)、Subject.cs(通知者)、Form1.cs

 

Observer.cs (事件订阅者)

class Observer     {         ///         /// 执行事件A         ///         ///         public string DoA()         {             return "时间到了,执行事件A~~";         }

        ///         ///执行事件B         ///         ///         public string DoB()         {             return "时间到了,执行事件B~~";         }

    }

 

 

Subject.cs(事件发布者)

namespace XXXXXX {     //声明委托     delegate string EventHandler();     class Subject     {         //声明事件         public event EventHandler Output;

       // 触发执行事件         public string Notify()         {             string res = "";             if (Output != null)             {                 res = Output();             }             return res;         }   } }

 

 

Form1.cs (事件订阅和触发)

使用了TextBox控件txtShow和Timer控件timer,timer的时间间隔设为1s

private void timer_Tick(object sender, EventArgs e)         {                         Subject subject = new Subject();             Observer observer = new Observer();             string now = DateTime.Now.ToString("HH:mm:ss");

           // 事件注册(订阅)           // 订阅者把自己的处理方法,向发布者进行委托事件的订阅            //设置固定时间要执行的事件             switch (now)             {                 case "22:28:00":                    subject.Output += new EventHandler(observer.DoA);                     break;                 case "22:29:00":                    subject.Output += new EventHandler(observer.DoB);                     break;             }             string res = "";             //触发执行事件             res += subject.Notify();             if (res != "")             {                 txtShow.AppendText(now + ":");                 txtShow.AppendText(res);                 txtShow.AppendText("\r\n");             } }

 

 结果:

 

 

但以上的方法是同步的,也就是第一个方法执行太久的话会影响第二个方法的执行,那么要解决这问题,下面就用到异步委托。

Observer.cs不用修改到,这也是用了观察者模式所带来的好处。

Subject.cs(修改了Notify方法,添加了一个委托、事件和方法)

namespace XXXX {    //声明委托     delegate string EventHandler();     delegate void ShowInfoHandler(string info);     class Subject     {        //声明事件         public event EventHandler Output;         public event ShowInfoHandler ShowInfoEvent;        

        public void Notify()         {                         if (Output != null)             {

                //获取事件中的多路委托列表

                Delegate[] delegAry =  Output.GetInvocationList();

 

                //遍历委托列表

                foreach( EventHandler handler in delegAry )                 {                     //异步调用委托,第一个参数为要回调的函数(执行完自身的方法后会继续执行的方法),第二个参数为要向回调函数传入的值(自身的委托,方便在回调函数中获取执行完返回的信息)                     //这里传入被调用方法的委托                     handler.BeginInvoke(CallBack,  handler);                                      }             }                      }

        ///         /// 回调函数         ///         ///         public void CallBack(IAsyncResult obj)         {             EventHandler handler = (EventHandler)obj.AsyncState;             //获取被调用方法的返回的信息             string res= handler.EndInvoke(obj);             ShowInfoEvent(res);         }

    } }

这里稍微解释一下。ShowInfoHandler、ShowInfoEvent用于向主线程txtShow输出提示信息用的,若不用输出提示信息可以省去。(Form1.cs会用到)

handler.BeginInvoke调用异步委托,第一个参数传入要回调的函数,也就是执行完自身的方法后会继续执行的方法;第二个参数一般传入自身的委托,方便在回调函数中获取执行完返回的信息。

 

Form1.cs

//非主线程无法操作界面的控件,所以用委托来实现向txtShow输出信息         public void ShowInfo(string info)         {             txtShow.Invoke(new Action(()=>{txtShow.AppendText(info+"\r\n");}));         }

        private void timer_Tick(object sender, EventArgs e)         {                                       Subject subject = new Subject();             Observer observer = new Observer();

            // 事件订阅             //将向订阅者txtShow输出信息的方法注册交给subject的委托 ShowInfoEvent             subject.ShowInfoEvent += new ShowInfoHandler(this.ShowInfo);             string now = DateTime.Now.ToString("HH:mm:ss");             switch (now)             {                 case "23:20:00":                     txtShow.AppendText("现在时间:"+now+"\r\n");                     subject.Output += new EventHandler(observer.DoA);                                          break;                 case "23:21:00":                     txtShow.AppendText("现在时间:"+now+"\r\n");                     subject.Output += new EventHandler(observer.DoB);                     break;             }

            // 触发事件             subject.Notify();                                   }

 

 

子线程操作主线程的控件还有其他方法,大家可以尝试下,这里就不整理了。

结果:

 

关注
打赏
1664521772
查看更多评论
立即登录/注册

微信扫码登录

0.0387s