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。

 

 1  public interface IAsyncResult2 {3     object AsyncState { get; }4     5     WaitHandle AsyncWaitHandle { get; }6     7     bool CompletedSynchronously { get; }8 9     bool IsCompleted { get; }
10 }

复制代码

上面的代码是IAsyncResult接口声明的四个属性:

1. AsyncState属性是一个用户定义的对象,包含异步操作状态信息。例如,当我们调用FileStream类的BeginRead()方法进行异步读取文件内容时,传入的最后一个参数对应的就是AsyncState属性。

2. AsyncWaitHandle属性主要的作用是阻塞当前线程来等待异步操作完成。WaitHandle抽象类,有一个很重要的派生类ManualResetEvent。

3. CompletedSynchronously属性比较特别,用来判断异步操作是否是同步完成(这个有点儿绕~)。

4. IsCompleted属性就比较简单了,用来判断异步操作是否完成,true表示已完成,false表示还未完成。

在实现IAsyncResult接口时,我们主要会用到AsyncState,IsCompleted和AsyncWaitHandle属性。

复制代码

  1 /// <summary>2 /// CalculatorAsyncResult<T>类,实现了IAsyncResult接口3 /// </summary>4 /// <typeparam name="T"></typeparam>5 public class CalculatorAsyncResult<T> : IAsyncResult6 {7     private ManualResetEvent _waitHandle;8 9     private object _asyncState;10 11     private bool _completedSynchronously;12 13     private bool _isCompleted;14 15     //我们传入的异步回调方法16     private AsyncCallback _asyncCallback;17 18     //保存异步操作返回结果19     public T CalulatorResult { get; set; }20 21     public static CalculatorAsyncResult<T> CreateCalculatorAsyncResult(Func<T> work, AsyncCallback asyncCallback, object obj)22     {23         var asyncResult = new CalculatorAsyncResult<T>(obj, asyncCallback, false, false);24 25         asyncResult.ExecuteWork(work);26 27         return asyncResult;28     }29 30     public CalculatorAsyncResult(object obj, AsyncCallback asyncCallback, bool completedSynchronously, bool isCompleted)31     {32         _waitHandle = new ManualResetEvent(false);33 34         _asyncState = obj;35 36         _completedSynchronously = completedSynchronously;37 38         _isCompleted = isCompleted;39 40         _asyncCallback = asyncCallback;41     }42 43     public object AsyncState44     { 45         get { return _asyncState; } 46     }47 48     public WaitHandle AsyncWaitHandle49     {50         get{ return _waitHandle; }51     }52 53     public bool CompletedSynchronously54     {55         get { return _completedSynchronously; }56     }57 58     public bool IsCompleted59     {60         get { return _isCompleted; }61     }62 63     public void Wait()64     {65         _waitHandle.WaitOne();66     }67 68     /// <summary>69     /// 调用异步回调方法70     /// </summary>71     private void InvokeAsyncCallback()72     {73         _isCompleted = true;74 75         if (_waitHandle != null)76         {77             _waitHandle.Set();78         }79         80         //调用我们传入的异步回调方法81         _asyncCallback(this);82     }83 84     /// <summary>85     /// 执行异步工作86     /// </summary>87     /// <param name="work"></param>88     public void ExecuteWork(Func<T> work)89     {90         if(_asyncCallback != null)91         {92             Task<T> task = Task.Factory.StartNew<T>(work);93 94             task.ContinueWith(t => 95             {96                 CalulatorResult = t.Result;97 98                 InvokeAsyncCallback();99             });
100         }
101         else 
102         {
103             _isCompleted = true;
104 
105             if(_waitHandle != null)
106             {
107                 _waitHandle.Set();
108             }
109         }
110     }
111 }

复制代码

2. 定义BeginXXX()和EndXXX()方法

下面就来定义我们自己的APM接口和具体实现类,编写BeginXXX()和EndXXX()方法。

复制代码

 1 /// <summary>2 /// 异步计算接口3 /// </summary>4 /// <typeparam name="T"></typeparam>5  public interface ICalculator<T>6  {7      IAsyncResult BeginAdd(T x, T y, AsyncCallback asyncCallback, Object obj);8 9      T EndAdd(IAsyncResult ar);
10  }

复制代码

复制代码

 1 /// <summary>2 /// 异步计算接口实现类3 /// </summary>4  public class Calculator : ICalculator<double>5 {6     public IAsyncResult BeginAdd(double x, double y, AsyncCallback asyncCallback, Object obj)7     {8         return CalculatorAsyncResult<double>.CreateCalculatorAsyncResult(delegate { return Add(x, y); }, asyncCallback, obj);9     }
10 
11     public double EndAdd(IAsyncResult ar)
12     {
13         var calculatorAsyncResult = (CalculatorAsyncResult<double>)(ar);
14 
15         calculatorAsyncResult.Wait();
16 
17         return calculatorAsyncResult.CalulatorResult;
18     }
19 
20     /// <summary>
21     /// 计算方法
22     /// </summary>
23     /// <param name="x"></param>
24     /// <param name="y"></param>
25     /// <returns></returns>
26     protected double Add(double x, double y)
27     {
28         Console.WriteLine("Async thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);
29 
30         Console.WriteLine("Async thread(id={0}) is calculating...\n", Thread.CurrentThread.ManagedThreadId);
31 
32         Thread.Sleep(3000);
33 
34         var r = x + y;
35 
36         Console.WriteLine("Async thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
37 
38         return r;
39     }
40 }

复制代码

3. 获取异步操作结果

APM提供了四种获取异步操作的结果方式供我们选择:

1. 通过IAsyncResult的AsyncWaitHandle属性,调用它的WaitOne()方法使调用线程阻塞来等待异步操作完成再调用EndXXX()方法来获取异步操作结果。

2. 在调用BeginXXX()方法的线程上调用EndXXX()方法来获取异步操作结果。这种方式也会阻塞调用线程(阻塞原理同方式1,具体在上面的代码中有体现)。

3. 轮询IAsyncResult的IsComplete属性,当异步操作完成后再调用EndXXX()方法来获取异步操作结果。

4. 使用 AsyncCallback委托来指定异步操作完成时要回调的方法,在回调方法中调用EndXXX()方法来获取异步操作结果。

在上述的四种方式中,只有第四种方式是完全不会阻塞调用线程的,所以多数情况下我们都会选择回调的方式来获取异步操作结果。

复制代码

 1  public class Program2 {3     public static double result = 0;4 5     static void Main(string[] args)6     {7         Console.WriteLine("Main thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);8 9         var calculator = new Calculator();
10 
11         Console.WriteLine("Main thread(id={0}) invokes BeginAdd() function.\n", Thread.CurrentThread.ManagedThreadId);
12 
13         calculator.BeginAdd(1, 2, Callback, calculator);
14 
15         Console.WriteLine("Main thread(id={0}) is sleeping...\n", Thread.CurrentThread.ManagedThreadId);
16 
17         Thread.Sleep(5000);
18 
19         Console.WriteLine("The calculating result of async operation is {0}.\n", result);
20 
21         Console.WriteLine("Main thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
22     }
23 
24     /// <summary>
25     /// 我们定义的回调方法
26     /// </summary>
27     /// <param name="ar"></param>
28     public static void Callback(IAsyncResult ar)
29     {
30         var calculator = (Calculator)(ar.AsyncState);
31 
32         result = calculator.EndAdd(ar);
33     }
34 }

复制代码

运行结果:

至此,我们已经完整地实现了APM异步编程模型,从运行结果中我们可以得出,通过回调的方式来获取异步操作结果是完全不会阻塞调用线程的。

总结

1. 实现APM的关键是实现IAsyncResult接口。在IAsyncResult实现类中,需要使用线程池来异步地执行操作,在操作完成之后,再调用传入的回调方法来返回操作结果。

2. 实现了APM的类中都会定义一对BeginXXX()和EndXXX()方法,开始异步操作,结束异步操作并返回异步操作结果。

3. 获取异步操作结果有四种方式,但是只有回调方式是完全不会阻塞调用线程的,其他的都会阻塞调用线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不会卸载MySQL?我连夜肝了一篇教你如何干干净净地卸载掉MySQL

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

设计模式篇

一. 什么是设计模式 纠结了好久&#xff0c;今天终于下定决心开始写设计模式系列&#xff0c;因为这个系列章节确实不好写&#xff0c;在这之前&#xff0c;也看了好多关于设计模式的博客、视频、书籍等&#xff0c;大多数用的例子要么猫啊狗啊、大雁等动物类&#xff1b;要么就…

Navicat15安装笔记

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏 &#x1f4c5;创作日期&#xff1a;2021年12月29日 &#x1f4c5;修改日期…

MySQL5安装配置笔记【超详细】

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

IDEA常用快捷键大合集

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

KnockoutJs篇:快速掌握KnockoutJs

一、引言 之前这个系列文章已经介绍Bootstrap。由于最近项目中&#xff0c;前端是Asp.net MVC KnockoutJs Bootstrap来做的。所以我又重新开始写这个系列。今天就让我们来看看Web前端的MVVM框架——KnockoutJs。 二、KnockoutJs是什么&#xff1f; 做.NET开发的人应该都知道…

IDEA常用设置【很实用】

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

【git下载安装与配置】

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

git 连接gitee时报错 Auth error: Access deined: authorize failure

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &#x1…

C#多线程编程系列(五)- 使用任务并行库

目录 1.1 简介1.2 创建任务1.3 使用任务执行基本的操作1.4 组合任务1.5 将APM模式转换为任务1.6 将EAP模式转换为任务1.7 实现取消选项1.8 处理任务中的异常1.9 并行运行任务1.10 使用TaskScheduler配置任务执行参考书籍笔者水平有限&#xff0c;如果错误欢迎各位批评指正&…

【VSCode快捷键大合集】

&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是超梦梦梦梦&#xff0c;很高兴认识大家~ &#x1f64f;如果本博文对小伙伴们有帮助的话&#xff0c;欢迎&#x1f50e;关注➕&#x1f91e;点赞➕&#x1f4cb;评论➕&#x1f604;收藏一波哦~ &…

浅谈Vue.js的优势

写在前面 今天小梦跟小伙伴们简简单单聊一下Vue.js的优势。小梦也是刚刚接触Vue.js&#xff0c;在学习一门新的技术之前&#xff0c;我们当然要了解其优势&#xff0c;知道优势在哪更加有利于我们去学习并转换为自己的储备。 浅谈Vue.js的优势 首先Vue.js是一个轻巧、高性能、…

async await 的前世今生(Updated)

async 和 await 出现在C# 5.0之后&#xff0c;给并行编程带来了不少的方便&#xff0c;特别是当在MVC中的Action也变成async之后&#xff0c;有点开始什么都是async的味道了。但是这也给我们编程埋下了一些隐患&#xff0c;有时候可能会产生一些我们自己都不知道怎么产生的Bug&…

MySQL八大约束

MySQL约束MySQL约束主键约束(primary key)自增长约束(auto_increment)非空约束(not null)唯一约束(unique)默认约束(default)零填充约束(zerofill)外键约束(foreign key)MySQL约束 概念 约束英文&#xff1a;constraint约束实际上就是表中数据的限制条件 作用 表在设计的时…

ASP.NET使用管道模型(PipleLines)处理HTTP请求

大多数人认为ASP.NET仅仅只是页面——使用模板来创建HTML页面然后返回给浏览器。但是这仅仅只是ASP.NET使用HTTP管道模型处理WEB程序很小的一方面。管道模型是类似于Web Services的一种在服务器端处理ASP.NET页面的框架技术。作为一名高级的ASP.NET的开发者&#xff0c;你必须清…

【LeetCode-SQL每日一练】——1.组合两个表

&#x1f388;写在前面 &#x1f64b;‍♂️大家好呀&#xff0c;我是超梦梦梦梦。小伙伴们都知道&#xff0c;不管是在学习中还是日常工作中&#xff0c;几乎天天是要跟数据库打交道的&#xff0c;为了更好的操作数据库&#xff0c;我们的SQL知识储备是必不可少的。想要掌握好…

【LeetCode-SQL每日一练】——2. 第二高的薪水

&#x1f388;写在前面 &#x1f64b;‍♂️大家好呀&#xff0c;我是超梦。小伙伴们都知道&#xff0c;不管是在学习中还是日常工作中&#xff0c;几乎天天是要跟数据库打交道的&#xff0c;为了更好的操作数据库&#xff0c;我们的SQL知识储备是必不可少的。想要掌握好SQL&am…

【LeetCode-SQL每日一练】—— 181. 超过经理收入的员工

&#x1f388;写在前面 &#x1f64b;‍♂️大家好呀&#xff0c;我是超梦。小伙伴们都知道&#xff0c;不管是在学习中还是日常工作中&#xff0c;几乎天天是要跟数据库打交道的&#xff0c;为了更好的操作数据库&#xff0c;我们的SQL知识储备是必不可少的。想要掌握好SQL&am…

【windows环境——VSCode安装教程】

大家好呀&#xff01;我是超梦&#xff0c;今天给小伙伴们带来一个最新版在windows环境的VSCode安装教程&#xff0c;话不多说我们开始吧。 VSCode安装下载与安装设置中文环境下载与安装 1. 第一步&#xff0c;进入官网&#xff0c;根据自己的电脑位数选择下载&#xff0c;小梦…

C#通用类Helper整理

★前言 最近下载了tita_chou在CSDN上传的一个资源&#xff0c;是在工作中整理的C#帮助类&#xff0c;里面包含了很多实用的类&#xff0c;想到我之前收集过自己用到少的可怜的类&#xff0c;心生敬意啊。当粗略的查看了那个资源&#xff0c;发现有一些是重复的&#xff0c;有一…