C#异步编程模型
什么是异步编程模型
异步编程模型(Asynchronous Programming Model,简称APM)是C#1.1支持的一种实现异步操作的编程模型,虽然已经比较“古老”了,但是依然可以学习一下的。通过对APM的学习,我总结了以下三点:
1. APM的本质是使用委托和线程池来实现异步编程的。
2. 实现APM的关键是要实现IAsyncResult接口。
3. 实现了APM的类都会定义一对形如BeginXXX()和EndXXX()的方法,例如,FileStream类定义了BeginRead()方法和EndRead()方法,可以实现异步读取文件内容。
下面我们就通过具体的代码来实现异步编程模型。
实现异步编程模型
1. 实现IAsyncResult接口
IAsyncResult接口是C#类库中定义的一个接口,表示异步操作的状态,具体介绍可以查看MSDN。
public interface IAsyncResult
{
object AsyncState { get; } WaitHandle AsyncWaitHandle { get; } bool CompletedSynchronously { get; } bool IsCompleted { get; }
}
上面的代码是IAsyncResult接口声明的四个属性:
1. AsyncState属性是一个用户定义的对象,包含异步操作状态信息。例如,当我们调用FileStream类的BeginRead()方法进行异步读取文件内容时,传入的最后一个参数对应的就是AsyncState属性。
2. AsyncWaitHandle属性主要的作用是阻塞当前线程来等待异步操作完成。WaitHandle抽象类,有一个很重要的派生类ManualResetEvent。
3. CompletedSynchronously属性比较特别,用来判断异步操作是否是同步完成(这个有点儿绕~)。
4. IsCompleted属性就比较简单了,用来判断异步操作是否完成,true表示已完成,false表示还未完成。
在实现IAsyncResult接口时,我们主要会用到AsyncState,IsCompleted和AsyncWaitHandle属性。
/// <summary>
/// CalculatorAsyncResult<T>类,实现了IAsyncResult接口
/// </summary>
/// <typeparam name="T"></typeparam>
public class CalculatorAsyncResult<T> : IAsyncResult
{
private ManualResetEvent _waitHandle; private object _asyncState; private bool _completedSynchronously; private bool _isCompleted; //我们传入的异步回调方法
private AsyncCallback _asyncCallback; //保存异步操作返回结果
public T CalulatorResult { get; set; } public static CalculatorAsyncResult<T> CreateCalculatorAsyncResult(Func<T> work, AsyncCallback asyncCallback, object obj)
{
var asyncResult = new CalculatorAsyncResult<T>(obj, asyncCallback, false, false); asyncResult.ExecuteWork(work); return asyncResult;
} public CalculatorAsyncResult(object obj, AsyncCallback asyncCallback, bool completedSynchronously, bool isCompleted)
{
_waitHandle = new ManualResetEvent(false); _asyncState = obj; _completedSynchronously = completedSynchronously; _isCompleted = isCompleted; _asyncCallback = asyncCallback;
} public object AsyncState
{
get { return _asyncState; }
} public WaitHandle AsyncWaitHandle
{
get{ return _waitHandle; }
} public bool CompletedSynchronously
{
get { return _completedSynchronously; }
} public bool IsCompleted
{
get { return _isCompleted; }
} public void Wait()
{
_waitHandle.WaitOne();
} /// <summary>
/// 调用异步回调方法
/// </summary>
private void InvokeAsyncCallback()
{
_isCompleted = true; if (_waitHandle != null)
{
_waitHandle.Set();
} //调用我们传入的异步回调方法
_asyncCallback(this);
} /// <summary>
/// 执行异步工作
/// </summary>
/// <param name="work"></param>
public void ExecuteWork(Func<T> work)
{
if(_asyncCallback != null)
{
Task<T> task = Task.Factory.StartNew<T>(work); task.ContinueWith(t =>
{
CalulatorResult = t.Result; InvokeAsyncCallback();
});
}
else
{
_isCompleted = true; if(_waitHandle != null)
{
_waitHandle.Set();
}
}
}
}
2. 定义BeginXXX()和EndXXX()方法
下面就来定义我们自己的APM接口和具体实现类,编写BeginXXX()和EndXXX()方法。
/// <summary>
/// 异步计算接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICalculator<T>
{
IAsyncResult BeginAdd(T x, T y, AsyncCallback asyncCallback, Object obj); T EndAdd(IAsyncResult ar);
}
/// <summary>
/// 异步计算接口实现类
/// </summary>
public class Calculator : ICalculator<double>
{
public IAsyncResult BeginAdd(double x, double y, AsyncCallback asyncCallback, Object obj)
{
return CalculatorAsyncResult<double>.CreateCalculatorAsyncResult(delegate { return Add(x, y); }, asyncCallback, obj);
} public double EndAdd(IAsyncResult ar)
{
var calculatorAsyncResult = (CalculatorAsyncResult<double>)(ar); calculatorAsyncResult.Wait(); return calculatorAsyncResult.CalulatorResult;
} /// <summary>
/// 计算方法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
protected double Add(double x, double y)
{
Console.WriteLine("Async thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Async thread(id={0}) is calculating...\n", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(); var r = x + y; Console.WriteLine("Async thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId); return r;
}
}
3. 获取异步操作结果
APM提供了四种获取异步操作的结果方式供我们选择:
1. 通过IAsyncResult的AsyncWaitHandle属性,调用它的WaitOne()方法使调用线程阻塞来等待异步操作完成再调用EndXXX()方法来获取异步操作结果。
2. 在调用BeginXXX()方法的线程上调用EndXXX()方法来获取异步操作结果。这种方式也会阻塞调用线程(阻塞原理同方式1,具体在上面的代码中有体现)。
3. 轮询IAsyncResult的IsComplete属性,当异步操作完成后再调用EndXXX()方法来获取异步操作结果。
4. 使用 AsyncCallback委托来指定异步操作完成时要回调的方法,在回调方法中调用EndXXX()方法来获取异步操作结果。
在上述的四种方式中,只有第四种方式是完全不会阻塞调用线程的,所以多数情况下我们都会选择回调的方式来获取异步操作结果。
public class Program
{
public static double result = ; static void Main(string[] args)
{
Console.WriteLine("Main thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId); var calculator = new Calculator(); Console.WriteLine("Main thread(id={0}) invokes BeginAdd() function.\n", Thread.CurrentThread.ManagedThreadId); calculator.BeginAdd(, , Callback, calculator); Console.WriteLine("Main thread(id={0}) is sleeping...\n", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(); Console.WriteLine("The calculating result of async operation is {0}.\n", result); Console.WriteLine("Main thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
} /// <summary>
/// 我们定义的回调方法
/// </summary>
/// <param name="ar"></param>
public static void Callback(IAsyncResult ar)
{
var calculator = (Calculator)(ar.AsyncState); result = calculator.EndAdd(ar);
}
}
运行结果:

至此,我们已经完整地实现了APM异步编程模型,从运行结果中我们可以得出,通过回调的方式来获取异步操作结果是完全不会阻塞调用线程的。
总结
1. 实现APM的关键是实现IAsyncResult接口。在IAsyncResult实现类中,需要使用线程池来异步地执行操作,在操作完成之后,再调用传入的回调方法来返回操作结果。
2. 实现了APM的类中都会定义一对BeginXXX()和EndXXX()方法,开始异步操作,结束异步操作并返回异步操作结果。
3. 获取异步操作结果有四种方式,但是只有回调方式是完全不会阻塞调用线程的,其他的都会阻塞调用线程。
C#异步编程模型的更多相关文章
- JS魔法堂:深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- 多线程之异步编程: 经典和最新的异步编程模型,async与await
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...
- 谈谈c#中异步编程模型的变迁
大家在编程过程中都会用到一些异步编程的情况.在c#的BCL中,很多api都提供了异步方法,初学者可能对各种不同异步方法的使用感到迷惑,本文主要为大家梳理一下异步方法的变迁以及如何使用异步方法. Beg ...
- 深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换
[源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...
- 【温故知新】c#异步编程模型(APM)--使用委托进行异步编程
当我们用到C#类许多耗时的函数XXX时,总会存在同名的类似BeginXXX,EndXXX这样的函数. 例如Stream抽象类的Read函数就有 public abstract int Read(byt ...
- 《C#并行编程高级教程》第9章 异步编程模型 笔记
这个章节我个人感觉意义不大,使用现有的APM(异步编程模型)和EAP(基于时间的异步模型)就很够用了,针对WPF和WinForm其实还有一些专门用于UI更新的类. 但是出于完整性,还是将一下怎么使用. ...
- C#异步编程の-------异步编程模型(APM)
术语解释: APM 异步编程模型, Asynchronous Programming Model EAP 基于事件的异步编程模式, Event ...
- .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)
本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...
随机推荐
- 十、Node.js-url模块
下面使用之前提到过的note交互模式(可以在cmd直接执行js代码)进行学习url模块 跳出note模式同样是Ctrl+C(两次) 学习url模块主要是要掌握url模块的方法: url.parse() ...
- 【BZOJ 1877】 [SDOI2009]晨跑(费用流)
题目描述 Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街 ...
- Java面向对象之多态(向上、向下转型) 入门实例
一.基础概念 多态: 重点是对象的多态性.某一事物的多种体现形态. 多态的作用: 1.提高了代码的扩展性,后期出现的功能,可以被之前的程序所执行. 2.不能使用子类特有的功能.只能使用覆盖父类的功能. ...
- Java面向对象之关键字final 入门实例
一.基础概念 1.关键字final可以修饰类.函数.变量. 2.关键字final修饰的类不可以被继承. 3.关键字final修饰的方法不可以被覆盖. 4.关键字final修饰的变量是一个常量,只能被赋 ...
- js 自定义属性
html标签中有没有什么自带的属性可以存储成绩的----没有 本身html标签没有这个属性,自己(程序员)添加的,----自定义属性---为了存储一些数据 在html标签中添加的自定义属性,如果 ...
- JS 对象的三种创建方式
变量 instanceof 类型的名字----->布尔类型,true就是这种类型,false不是这种类型 在当前的对象的方法中,可以使用this关键字代表当前的对象 1.调用系统的构造函数创 ...
- ubuntu 16.04 安装googlepinyin中文输入法
安装谷歌拼音输入法 打开终端输入: apt-get install fcitx-googlepinyin 安装完成之后,进入系统设置 安装语言包 修改输入法系统 点击“System Setting”- ...
- [BZOJ 4923][Lydsy1706月赛]K小值查询
传送门 势能分析平衡树,splay或treap都可以 放个指针版的就跑 #include <bits/stdc++.h> using namespace std; #define rep( ...
- SQL Server2012如何更改服务器的名称
Problem: sql server 2012 安装完毕后,连接数据库只能使用 机器名\数据库实例 的方式,想用 localhost 或 . 作为服务器名称不可以 Solution: 参考:http ...
- Tomcat SSL证书安装配置
[From Internet] 首先找到安装Tomcat 目录下该文件“Server.xml”,一般默认路径都是在Conf 文件夹中.然后用文本编辑器打开该文件,接着找到 <Connector ...