C#异步之APM模式异步程序开发的示例分享

c#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,c#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下c#中的异步编程的发展历程。广告一下:喜欢我文章的朋友,请点下面的“关注我”。谢谢

我是2004年接触并使用C#的,那时C#版本为1.1,所以我们就从就那个时候谈起。那时在大学里自己看书写程序,所写的程序大都是同步程序,最多启动个线程……..其实在C#1.1的时代已有完整的异步编程解决方案,那就是APM(异步编程模型)。如果还有不了解“同步程序、异步程序”的请自行百度哦。

APM异步编程模型最具代表性的特点是:一个异步功能由以Begin开头、End开头的两个方法组成。Begin开头的方法表示启动异步功能的执行,End开头的方法表示等待异步功能执行结束并返回执行结果。下面是一个模拟的实现方式(后面将编写标准的APM模型异步实现):

public class Worker    {            public int A { get; set; }            public int B { get; set; }            private int R { get; set; }        ManualResetEvent et;                public void BeginWork(Action action)        {            et = new ManualResetEvent(false);                        new Thread(() =>            {                R = A + B;                Thread.Sleep(1000);                et.Set();                                if(null != action)                {                    action();                }            }).Start();        }        public int EndWork()        {            if(null == et)            {                t            hrow new Exception("调用EndWork前,需要先调用BeginWork");            }                        else            {                et.WaitOne();                                return R;            }        }     }

登录后复制

        static void Main(string[] args)        {           Worker w = new Worker();            w.BeginWork(()=> {                Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,                    w.EndWork());            });            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.ReadLine();        }

登录后复制

在上面的模拟APM模型中我们使用了 Thread、ManualResetEvent,如果你对多线程和ManualResetEvent不熟悉C#中使用异步编程不可避免的会涉及到多线程的知识,虽然微软在Framework中做了很多封装,但朋友们应该掌握其本质。

上面模拟的APM异步模型之所以简单,是因为C#发展过程中引入了很多优秀的语法规则。上例我们较多的使用了Lambda表达式,如果你不熟悉 匿名委托与lambda表达式可看我之前的Bolg《匿名委托与Lambda表达式》。上面做了如此多的广告,下面我们来看一下标准的APM模型如何实现异步编程。

IAsyncResult接口

IAsyncResult接口定义了异步功能的状态,该接口具体属性及含义如下:

   //     表示异步操作的状态。    [ComVisible(true)]    public interface IAsyncResult    {        //        // 摘要:        //     获取一个值,该值指示异步操作是否已完成。        //        // 返回结果:        //     如果操作已完成,则为 true;否则为 false。        bool IsCompleted { get; }        //        // 摘要:        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。        //        // 返回结果:        //     用于等待异步操作完成的 System.Threading.WaitHandle。        WaitHandle AsyncWaitHandle { get; }        //        // 摘要:        //     获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。        //        // 返回结果:        //     一个用户定义的对象,限定或包含有关异步操作的信息。        object AsyncState { get; }        //        // 摘要:        //     获取一个值,该值指示异步操作是否同步完成。        //        // 返回结果:        //     如果异步操作同步完成,则为 true;否则为 false。        bool CompletedSynchronously { get; }    }

登录后复制

注意:模型示例1中的 ManualResetEvent 继承自 WaitHandle
APM传说实现方式
在了解了IAsyncResult接口后,我们来通过实现 IAsyncResult 接口的方式完成对模拟示例的改写工作,代码如下:

    public class NewWorker    {        public class WorkerAsyncResult : IAsyncResult        {            AsyncCallback callback;                        public WorkerAsyncResult(int a,int b, AsyncCallback callback, object asyncState) {                A = a;                B = b;                state = asyncState;                                this.callback = callback;                                new Thread(Count).Start(this);            }                        public int A { get; set; }                        public int B { get; set; }                        public int R { get; private set; }                        private object state;                        public object AsyncState            {                            get                {                                    return state;                }            }                        private ManualResetEvent waitHandle;                        public WaitHandle AsyncWaitHandle            {                            get                {                                    if (null == waitHandle)                    {                        waitHandle = new ManualResetEvent(false);                    }                                        return waitHandle;                }            }            private bool completedSynchronously;                        public bool CompletedSynchronously            {                get                {                                    return completedSynchronously;                }            }                        private bool isCompleted;                        public bool IsCompleted            {                            get                {                                    return isCompleted;                }            }                        private static void Count(object state)            {                            var result = state as WorkerAsyncResult;                result.R = result.A + result.B;                Thread.Sleep(1000);                result.completedSynchronously = false;                result.isCompleted = true;                ((ManualResetEvent)result.AsyncWaitHandle).Set();                                if (result.callback != null)                {                    result.callback(result);                }            }        }                public int Num1 { get; set; }                public int Num2 { get; set; }                public IAsyncResult BeginWork(AsyncCallback userCallback, object asyncState)        {            IAsyncResult result = new WorkerAsyncResult(Num1,Num2,userCallback, asyncState);                        return result;        }        public int EndWork(IAsyncResult result)        {            WorkerAsyncResult r = result as WorkerAsyncResult;            r.AsyncWaitHandle.WaitOne();            return r.R;        }    }

登录后复制

示例代码分析:

上面代码中NewWorker的内部类 WorkerAsyncResult 是关键点,它实现了 IAsyncResult 接口并由它来负责开启新线程完成计算工作。

在WorkerAsyncResult中增加了 A、B两个私有属性来存储用于计算的数值,一个对外可读不可写的属性R,用于存储WorkerAsyncResult内部运算的结果。AsyncWaitHandle属性由 ManualResetEvent 来充当,并在首次访问时创建ManualResetEvent(但不释放)。其他接口属性正常实现,没有什么可说。

WorkerAsyncResult 中新增 static Count 方法,参数 state 为调用Count方法的当前WorkerAsyncResult对象。Count 方法在 WorkerAsyncResult 对象的新启线程中运行,因此Thread.Sleep(1000)将阻塞新线程1秒中。然后设置当前WorkerAsyncResult对象是否同步完成为false,异步完成状态为true,释放ManualResetEvent通知以便等待线程获取通知进入执行状态,判断是否有异步执行结束回调委托,存在则回调之。

NewWorker 非常简单,Num1、Num2两个属性为要计算的数值。BeginWork 创建WorkerAsyncResult对象、并将要计算的两个数值Num1、Num2、userCallback回调委托、object 类型的 asyncState 传入要创建的WorkerAsyncResult对象。经过此步操作,WorkerAsyncResult对象获取了运算所需的所有数据、运算完成后的回调,并马上启动新线程进行运算(执行WorkerAsyncResult.Count方法)。

因为WorkerAsyncResult.Count执行在新线程中,在该线程外部无法准确获知新线程的状态。为了满足外部线程与新线程同步的需求,在NewWorker中增加EndWork方法,参数类型为IAsyncResult。要调用EndWork方法应传入BeginWork 获取的WorkerAsyncResult对象,EndWork方法获取WorkerAsyncResult对象后,调用WorkerAsyncResult.AsyncWaitHandle.WaitOne()方法,等待获取ManualResetEvent通知,在获取到通知时运算线程已运算结束(线程并未结束),下一步获取运算结果R并返回。

接下来是NewWorker调用程序,如下:

        static void Main(string[] args)        {            NewWorker w2 = new NewWorker();            w2.Num1 = 10;            w2.Num2 = 12;            IAsyncResult r = null;            r = w2.BeginWork((obj) => {            Console.WriteLine("Thread Id:{0},Count:{1}",Thread.CurrentThread.ManagedThreadId,            w2.EndWork(r));            }, null);            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.ReadLine();        }

登录后复制

C#异步之APM模式异步程序开发的示例分享

下图我简单画的程序调用过程,有助于各位朋友理解:

C#异步之APM模式异步程序开发的示例分享

标准的APM模型异步编程,对应开发人员来说过于复杂。因此通过实现 IAsyncResult 接口进行异步编程,就是传说中的中看不中用(罪过罪过…..)。

Delegate异步编程(APM 标准实现)

C#中委托天生支持异步调用(APM模型),任何委托对象后”.”就会发现BeginInvoke、EndInvoke、Invoke三个方法。BeginInvoke为异步方式调用委托、EndInvoke等待委托的异步调用结束、Invoke同步方式调用委托。因此上面的标准APM实例,可借助  delegate 进行如下简化。

上面NewWorker使用委托方式改写如下:


登录后复制

    public class NewWorker2    {        Func action;        public NewWorker2()        {            action = new Func(Work);        }        public IAsyncResult BeginWork(AsyncCallback callback, object state)        {            dynamic obj = state;            return action.BeginInvoke(obj.A, obj.B, callback, this);        }        public int EndWork(IAsyncResult asyncResult)        {            try            {                return action.EndInvoke(asyncResult);            }            catch (Exception ex)            {                throw ex;            }        }        private int Work(int a, int b)        {            Thread.Sleep(1000);            return a + b;        }    }

登录后复制

调用程序:

        static void Main(string[] args)        {            NewWorker2 w2 = new NewWorker2();            IAsyncResult r = null;            r = w2.BeginWork((obj) =>            {                Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,                    w2.EndWork(r));            }, new { A = 10, B = 11 });            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.ReadLine();        }

登录后复制

上面的使用委托进行APM异步编程,比实现 IAsyncResult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 IAsyncResult 接口的传说方式看你的喜好吧。 

以上就是C#异步之APM模式异步程序开发的示例分享的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2493643.html

(0)
上一篇 2025年3月5日 01:35:16
下一篇 2025年3月5日 01:35:33

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • C#中关于Cookies的读取实例详解

    C#中Cookies的读取 链接: 一 、写入Cookie   1. Name 和 Value 属性由程序设定,默认值都是空引用。   2. Domain属性的默认值为当前URL的域名部分,不管发出这个cookie的页面在哪个目录下的。 D…

    编程技术 2025年3月5日
    000
  • C#中关于async与await的使用详解

    本篇文章主要介绍了说说c#的async和await的具体用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 C# 5.0中引入了async 和 await。这两个关键字可以让你更方便的写出异步代码。 看个例子:…

    编程技术 2025年3月5日
    200
  • C#中String类型与json之间相互转换的实现方法

    这篇文章主要介绍了c#实现string类型和json之间的相互转换功能,涉及c# json格式数据的构造、转换相关操作技巧,需要的朋友可以参考下 本文实例讲述了C#实现String类型和json之间的相互转换功能。分享给大家供大家参考,具体…

    编程技术 2025年3月5日
    200
  • C# Facade外观模式中天河城购物出现的问题解决

    这篇文章主要介绍了c#设计模式之facade外观模式解决天河城购物问题,简单描述了外观模式的定义并结合具体实例分析了外观模式解决购物问题的相关步骤与操作技巧,需要的朋友可以参考下 本文实例讲述了C#设计模式之Facade外观模式解决天河城购…

    2025年3月5日
    200
  • C#中Builder生成器模式解决配置电脑的问题详解

    这篇文章主要介绍了c#设计模式之builder生成器模式解决带老婆配置电脑问题,简单介绍了生成器模式的概念、功能并结合具体实例形式分析了c#生成器模式解决配电脑问题的步骤与相关操作技巧,需要的朋友可以参考下 本文实例讲述了C#设计模式之Bu…

    2025年3月5日
    200
  • C#实现Json序列化删除null值的方法实例

    要将一个对象序列化,可是如果对象的属性为null的时候,我们想将属性为null的都去掉,怎么处理呢?其实方法很简单的,下面就跟随脚本之家小编一起学习c#中 json 序列化去掉null值的方法吧 要将一个对象序列化,可是如果对象的属性为nu…

    编程技术 2025年3月5日
    200
  • C#中发送邮件的实现方法详解

    这篇文章主要为大家详细介绍了c#实现发送邮件的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 #region 发送邮件部分    private …

    编程技术 2025年3月5日
    200
  • C#中关于Minutes与TotalMinutes的区别

    今天测试提了一个bug,说是消息提醒的时机不对,设置的提前2小时,还没到就提醒了。 看了下代码 (m.ExpectReceiveTime – DateTime.Now).Minutes 执行(m.ExpectReceiveTime – Da…

    编程技术 2025年3月5日
    200
  • C#中关于表达式树的简单介绍

    表达式树可以说是linq的核心之一,为什么是linq的核心之一呢?因为表达式树使得c#不再是仅仅能编译成il,我们可以通过c#生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台上的本机语言。比如sql。我们常用的linq to …

    2025年3月5日
    200
  • C# WinForm跨线程访问控件的图文详解

     问题出现:  在WinForm 处理多线程访问主线程的控件时候,就会出现如图所示的错误对话框:          解决方案:      方案一:去掉线程访问主线程UI控件的安全检查,使用:  Control.CheckForIllegal…

    2025年3月5日 编程技术
    200

发表回复

登录后才能评论