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();
et.Set();
if(null != action)
{
action();
}
}).Start();
} public int EndWork()
{
if(null == et)
{
throw 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不熟悉,请阅读我之前的Blog《C#多线程的用法系列-线程间的协作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();
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 = ;
w2.Num2 = ;
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();
}

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

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

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

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

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

    public class NewWorker2
{
Func<int, int, int> action;
public NewWorker2()
{
action = new Func<int, int, int>(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();
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 = , B = });
Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine();
}

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

同时上面的delegate中我们还用了一些委托、匿名对象、动态类型等知识,如果你感兴趣的话都可从我的Blog中找到相关知识供你参考。

最后再广告一次:喜欢我文章的朋友请关注一下我的blog,谢谢。

C# 异步编程1 APM 异步程序开发的更多相关文章

  1. C# 异步编程2 EAP 异步程序开发

    在前面一篇博文记录了C# APM异步编程的知识,今天再来分享一下EAP(基于事件的异步编程模式)异步编程的知识.后面会继续奉上TPL任务并行库的知识,喜欢的朋友请持续关注哦. EAP异步编程算是C#对 ...

  2. C# 异步编程1 APM模式异步程序开发

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

  3. 转:[你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0——C#4.0中一些 ...

  4. [你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中 介绍了从C#1.0——C#4.0中一 ...

  5. 一、异步编程模型(APM)

    一.概念 APM即异步编程模式的简写(Asynchronous Programming Model).大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXX ...

  6. Java 异步编程 (5 种异步实现方式详解)

    ​ 同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现@mikechen 目录 什么是异步? 一.线程异步 二.Future异步 三.Comp ...

  7. php为什么需要异步编程?php异步编程的详解(附示例)

    本篇文章给大家带来的内容是关于php为什么需要异步编程?php异步编程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我对 php 异步的知识还比较混乱,写这篇是为了 ...

  8. Asp.Net异步编程-使用了异步,性能就提升了吗?

    Asp.Net异步编程 写在前面的话,很久没有写Blog了,不对,其实一致就没有怎么写过.今天有空,我也来写一篇Blog 随着.Net4.5的推出,一种新的编程方式简化了异步编程,在网上时不时的也看到 ...

  9. 异步编程模型(APM)模式

    什么是APM .net 1.0时期就提出的一种异步模式,并且基于IAsyncResult接口实现BeginXXX和EndXXX类似的方法. .net中有很多类实现了该模式(比如HttpWebReque ...

随机推荐

  1. iOS-SVProgressHUDMaskType

    SVProgressHUDMaskTypeNone:默认图层样式,当HUD显示的时候,允许用户交互.   SVProgressHUDMaskTypeClear:当HUD显示的时候,不允许用户交互.   ...

  2. null、undefined、typeof、instanceof

    目录 概述 null undefined typeof instanceof 概述 JavaScript(ECMAScript标准)里共有5种基本类型: undefined, null, Boolea ...

  3. Hadoop生态系统之HDFS

    一.介绍 HDFS : 分布式文件系统(distributed filesystem),主从结构. 以流式数据访问模式来存储超大文件,运行于商用硬件集群上. 超大文件: 几百M,几百G,甚至几百TB大 ...

  4. Spring Cloud 微服务分布式链路跟踪 Sleuth 与 Zipkin

    Zipkin 是一个开放源代码分布式的跟踪系统,由 Twitter 公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集.存储.查找和展现.它的理论模型来自于Google ...

  5. Spring Cloud Eureka 服务注册列表显示 IP 配置问题

    服务提供者向 Eureka 注册中心注册,默认以 hostname 的形式显示,Eureka 服务页面显示的服务是机器名:端口,并不是IP+端口的形式 ,可以通过修改服务提供者配置自己的 IP 地址, ...

  6. mysql 开发进阶篇系列 25 数据库RPM安装目录介绍

    一.概述 mysql可以在多个平台上运行,在windows平台上安装有noinstall包和图形化包二种方式.在linux/unix平台上有RPM包安装,二进制包(Binary Package)安装, ...

  7. Android应用内实现视频播放--腾讯浏览服务(TBS)

    TBS视频播放 TBS视频播放器可以支持市面上几乎所有的视频格式,包括mp4, flv, avi, 3gp, webm, ts, ogv, m3u8, asf, wmv, rm, rmvb, mov, ...

  8. Debug EOS:nodeos + mongo_db_plugin

    上文书说到区块链的存储方式,并结合了EOSIO进行分析,其中也提到了使用CLion调试EOS的方法.本文将继续深入细致地展开对加载了mongo_db_plugin的nodeos的调试过程以及心得. 关 ...

  9. CEF C++环境搭建

    第一步:下载CEF 到这里下载最新版本的CEF http://cefbuilds.com/ 下载解压之后,大概会看到如下图所示的文件 cefclient:是一个比较复杂的示例代码cefsimple:是 ...

  10. Java事件处理机制(深入理解)

    本文是关于Java事件处理机制的梳理,以及有重点的介绍一些注意点,至于基础的概念啥的不多赘述. 一.Java事件处理机制初步介绍(看图理解) 根据下图,结合生活实际,可以得知监护人可以有多个,坏人对小 ...