C#多线程之旅(4)——APM初探

源码地址:https://github.com/Jackson0714/Threadshtml

 

原文地址:C#多线程之旅(4)——APM初探git

C#多线程之旅目录:github

C#多线程之旅(1)——介绍和基本概念面试

C#多线程之旅(2)——建立和开始线程编程

C#多线程之旅(3)——线程池多线程

C#多线程之旅(4)——APM初探异步

C#多线程之旅(5)——同步机制介绍异步编程

C#多线程之旅(6)——详解多线程中的锁工具

更多文章正在更新中,敬请期待......post

 

C#多线程之旅(4)——APM初探

v博客前言

先交代下背景,前面几张内容主要是介绍多线程的基本知识,这一章是由于正好接触到了APM(异步编程模型),发现APM真的很强大,其中有部分知识点涉及到了委托的BeginInvoke/EndInvoke,就由衷地想写下APM相关的知识。

v写在前面

强大的异步处理模型,不得不被它折服!

v正文开始

1、简单的串行执行程序

 咱们先来看一个简单的程序:
定义了一个 int Add(int num),传入循环的次数num,返回循环相加的结果sum。
Step 1.Main方法调用Add方法,循环执行了2次,因此延时了2s,返回结果sum=1,打印 Result:1;
Step 2.Main方法循环执行了3次,延时了3s。
友情提醒:若是以为不想阅读多彩的 Console打印代码,能够选择查看下面折叠的code区域。 查看简洁版
 去掉颜色打印的code简洁版

 让咱们看看这个程序的运行结果:

 咱们能够从结果中看到:
  1.执行Add方法,是主线程执行Add方法;
  2.执行Main方法,是主线程执行Main方法;
  3.这中限时操做能够称为“计算限制的异步操做”;
  4.Add方法中模拟耗时操做(2s)和Main方法中模拟耗时操做(3s)是串行执行的,那么咱们有没有一种方法使这两种操做并行执行了?(3s中以内搞定这两个耗时操做)。答案是能够用APM。
 
 下面咱们用APM方式来节省2s的时间。

 2、使用委托来实现APM

2.1 预备知识

咱们使用泛型委托来实现APM,那么咱们须要点预备知识(对委托很熟练的同窗们能够跳过预备知识):
  1.什么是委托?
  2.什么是泛型委托?
  3.为何使用委托来实现APM?
对于这知识点一、2,能够参考我以前写的博客,在这里就再也不说明了, 不惧面试:委托
对于第三个知识点,是由于委托定义了两个异步方法 BeginInvokeEndInvoke
咱们能够先看看泛型委托的定义:
/// <summary>
/// 定义一个泛型委托
/// </summary>
/// <typeparam name="T">输入参数</typeparam>
/// <typeparam name="TResult">返回值</typeparam>
/// <param name="arg">输入参数</param>
/// <returns name="TResult">返回值</returns>
private delegate TResult Func<T, TResult>(T arg);

对于这个定义,C#编译器会将这行代码编译成一个类定义,它的逻辑定义以下:

public sealed class Func<T, TResult> : MulticastDelegate
{
	public Func(Object obj, IntPtr method);
	public TResult Invoke(T arg);
	public IAsyncResult BeginInvoke(T arg, AsyncCallback callback, Object obj);
	public TResult EndInvoke(IAsyncResult result);
}

定义一个委托时,会生成一个BeginInvoke和EndInvoke方法的类。

当定义下面的委托时

public delegate void myDelegate(int value);

经过反编译工具ILSpy查看结果:

BeginInvoke:

  1.第一个参数arg为委托定义相同的参数(能够为两个参数arg,和委托的签名相同),能够传入到委托引用的方法;

  2.倒数第二个参数callback为回调方法,当BeginInvoke方法执行完后,会当即调用回调方法,若是callback=null,则不调用回调方法;

  3.倒数第一个参数object给EndInvoke用的。

  4.返回值为IAsyncResult类型的接口对象(其实是AsynResult的类型实例)。该接口对象用途

    a.传递参数,它包含了对调用了BeginInvoke的委托的引用,这里是Add方法的int类型的输入参数;

    b.包含了BeginInvoke()的最后一个Object类型的参数

    c.它能够鉴别是哪一个方法的哪一次调用,由于经过同一个委托变量能够对同一个方法调用屡次。

EndInvoke

  1.第一个参数接收BeginInvoke返回的IAnsyResult;

  2.返回的TResult为委托引用的方法的返回值,这里是Add方法的int类型返回值

2.2 用委托来实现APM的原理

2.3 动手写个实现了APM的Code

经过上面的流程图,相信咱们对委托来实现APM有了必定的理解,再来读读code,相信能更快地理解。注释仅做参考,有问题能够回复我哦!

让咱们看看结果:

 注意:

  1.必须先将IAsyncResult转换为AsyncResult,才能获取到引用的委托,由于它没有包含在IAsyncResult接口的定义中;

  2.Add方法的调用,AddCallback方法都是线程池线程调用的;

  3.BeginInvoke的object参数能够为任何类型,例子中传递的是string类型的参数"I'm here!";

  4.主线程执行的for循环和Add方法中线程是同时进行的,交替打印结果;

  5.当异步的Add方法没有执行完毕,调用EndInvoke,则会阻塞当前线程池线程,只有异步方法执行完毕后,才会继续执行的代码;

  6.Add方法执行完后,会自动调用回调方法AddCallback;

  7.在调用EndInvoke可能抛出异常,因此须要加try/catch/finally,捕获EndInvoke的可能抛出的异常。

 
v写在最后

由于只是刚开始接触APM相关的知识,因此本篇只是写初探的内容,后面的章节会更多地介绍这方面的内容。但愿获得园友们的支持!


做  者: Jackson0714
出  处:http://www.cnblogs.com/jackson0714/
关于做者:专一于微软平台的项目开发。若有问题或建议,请多多赐教!
版权声明:本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。
特此声明:全部评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
声援博主:若是您以为文章对您有帮助,能够点击文章右下角推荐一下。您的鼓励是做者坚持原创和持续写做的最大动力!

相关文章
相关标签/搜索