最近在看一个线程框架,对.Net的异步编程模型很感兴趣,所以在这里实现CLR定义的异步编程模型,在CLR里有三种异步模式如下,如果不了解的可以详细看MSDN 文档Asynchronous programming patterns

1.Asynchronous Programming Model (APM)异步编程模式(也叫 IAsyncResult 模式),

public class MyClass
{
public IAsyncResult BeginRead(byte [] buffer, int offset, int count,AsyncCallbackcallback, object state);
public int EndRead(IAsyncResult asyncResult);
}

2.Event-based Asynchronous Pattern (EAP)基于事件的异步模式(客户端应用程序善于)

public class MyClass
{
public void ReadAsync(byte [] buffer, int offset, int count);
public event ReadCompletedEventHandler ReadCompleted;
}

3.Task-based Asynchronous Pattern (TAP)基于任务的异步模式(async 和await关键字)

public class MyClass
{
public Task<int> ReadAsync(byte [] buffer, int offset, int count);
}

现在我们基于第一种模式APM模式来自己实现一个异步模式,首先我们需要接触APM的一个重要接口IAsyncResult,他有四个属性需要实现。

namespace System {

    public interface IAsyncResult {

        object? AsyncState { get; }

        WaitHandle AsyncWaitHandle { get; }

        bool CompletedSynchronously { get; }

        bool IsCompleted { get; }
}
}

这四个对象分别有着自己的功能,IsCompleted是为了轮询查询状态,AsyncWaitHandle 是为了线程同步,AsyncState 是为了回调技术。拥有了这三个对象就可以做一个异步机制。首先我们实现这个接口。

 public class DelayTaskAsyncResult : IAsyncResult {

        private AsyncCallback _callback;
private object _asyncState;
private ManualResetEvent _resetEvent = new ManualResetEvent(false); public object result { get; set; } public DelayTaskAsyncResult(AsyncCallback callback, object state) {
this._callback = callback;
this._asyncState = state;
} public volatile int _completed = 0; public void SetCompleted() { Interlocked.Increment(ref _completed);
_resetEvent.Set(); _callback?.Invoke(this);
} public object EndInvoke() { if (!IsCompleted) {
AsyncWaitHandle.WaitOne();
}
return result;
} public object AsyncState => _asyncState; public WaitHandle AsyncWaitHandle => _resetEvent; public bool CompletedSynchronously => throw new NotImplementedException(); public bool IsCompleted => _completed != 0; }

很简单的实现如上,首先来解释一下这段代码,_callback和_asyncState是作为回调技术使用的,_resetEvent是为了线程同步技术使用的,result接口是异步处理后得到的结果,_completed作为线程处理状态的标记,加了volatile保证原子性保证多线程模式下拿到的值是最新的,SetCompleted方法是在线程执行完毕之后执行更新IAsyncResult其中的状态,先将状态值_completed自增,然后设置通过的信号量,有回调方法执行回调,而EndInvoke方法中如果没有执行完就等待信号量,如果执行完就返回执行结果。

现在接口已经实现完成,现在需要定义自己想要的任务对象,在这里我模拟了一个异步对象在线程里做一些耗时操作如下。

    public class DelayTask {

        public int _seconds { get; set; }

        public DelayTask(int seconds) {
_seconds = seconds;
} public IAsyncResult BeginDelay(AsyncCallback callback,object state) { var result = new DelayTaskAsyncResult(callback,state); ThreadPool.QueueUserWorkItem(_delayCore, result); return result;
} public object EndDelay(IAsyncResult asyncResult) { var result = (DelayTaskAsyncResult)asyncResult; return result.EndInvoke();
} private void _delayCore(object obj) { var asyncResult = (DelayTaskAsyncResult)obj; Thread.Sleep(_seconds * 1000); asyncResult.result = DateTime.Now; asyncResult.SetCompleted();
} }

在DelayTask里,BeginDelay接受两个参数AsyncCallback和object,这两个参数是为了回调机制使用的,然后创建一个异步结果DelayTaskAsyncResult传入另一个线程执行_delayCore,在_delayCore执行一个耗时操作然后将结果赋予result对象并更新状态SetCompleted,在EndDelay里,调用EndInvoke去同步异步结果。

使用方式如下

        public static void Main(string[] args) {

            DelayTask task = new DelayTask(5);

            var asyncResult = task.BeginDelay(null, null);

            Console.WriteLine("main execute");

            Console.WriteLine("other end at " + task.EndDelay(asyncResult));

            Console.Read();

        }

        //execute result:
//main execute
//consume time operation
//other end at 2021/6/3 20:51:18

这个实现了异步操作并没有block main thread,直到调用EndDelay block得到执行结果。下一步再看一下异步回调方法的使用。

        public static void Main(string[] args) {

            DelayTask task = new DelayTask(5);

            var asyncResult = task.BeginDelay(TaskCompleteCallBack, task);

            Console.WriteLine("main execute");

            Console.Read();

        }

        private static void TaskCompleteCallBack(IAsyncResult ar) {

            var task = (DelayTask)ar.AsyncState;

            Console.WriteLine("other end at " + task.EndDelay(ar));
}

效果和上面一样,值得注意的是异步的时候回调方法是执行在另一个线程上。 好了,APM的模式实现我们已经完成了。

现在我们看第二种的EAP的实现方式,基于事件的异步编程模式。这在富客户端应用程序大展拳脚。他的实现非常简单。

        public delegate void TaskCompletedEventHandler(object sender, TaskCompletedEventArg e);

        public class DelayTask1 {

            private int _seronds;

            public DelayTask1(int seronds) {
_seronds = seronds;
} public event TaskCompletedEventHandler TaskCompletedEventHandler; public void DoTaskAsync(string str) { ThreadPool.QueueUserWorkItem(TaskHelper,str);
} private void TaskHelper(object state) { var text = (string)state; Thread.Sleep(_seronds*1000); var result = DateTime.Now.ToString("yyyy-mm-dd")+text; TaskCompletedEventHandler.Invoke(this,new TaskCompletedEventArg {
Result= result
});
}
}

首先定义一个委托,然后用这个委托声明事件,委托定义了一个事件参数是为了回调使用,然后TaskHelper就是异步执行的方法,基于事件的实现因为没有异步对象IAsyncResult实现的非常清晰。调用如下。

        public static void Main(string[] args) {

            var task = new DelayTask1(5);

            task.TaskCompletedEventHandler += TaskCompleteCallBack;

            task.DoTaskAsync(" by neil");

            Console.WriteLine("main execute");

            Console.Read();

        }

        private static void TaskCompleteCallBack(object sender, TaskCompletedEventArg e) {

            Console.WriteLine("other end at"+ e.Result);
} //main execute
//consume time operation
//other end at2021-11-03 by neil

EAP模式的例子非常清晰,大家可以运行就可。

现在我们使用第三种的TAP的异步编程模型非常多,不管是富客户端还是asp.net core中,这是因为编译器在中间做了大量的工作,async和await关键字会将代码分为同步和回调,这个模式的实现还是需要反编译源码去知道编译器做了哪些动作。以后有时间我会和大家探讨一下这其中的原理。

好了今天就写到这里了,如果大家有任何不明白的地方欢迎评论留言,最后谢谢大家的阅读。

.Net Core自实现CLR异步编程模式(Asynchronous programming patterns)的更多相关文章

  1. C#的多线程——使用async和await来完成异步编程(Asynchronous Programming with async and await)

    https://msdn.microsoft.com/zh-cn/library/mt674882.aspx 侵删 更新于:2015年6月20日 欲获得最新的Visual Studio 2017 RC ...

  2. 游戏编程模式 Game Programming Patterns (Robert Nystrom 著)

    第1篇 概述 第1章 架构,性能和游戏 (已看) 第2篇 再探设计模式 第2章 命令模式 (已看) 第3章 享元模式 (已看) 第4章 观察者模式 (已看) 第5章 原型模式 (已看) 第6章 单例模 ...

  3. C#多线程和异步(三)——一些异步编程模式

    一.任务并行库 任务并行库(Task Parallel Library)是BCL中的一个类库,极大地简化了并行编程,Parallel常用的方法有For/ForEach/Invoke三个静态方法.在C# ...

  4. .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)

    本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...

  5. C#中的异步编程模式

    异步编程模型(APM) 基于事件的异步编程模式 基于任务的异步模式 Async Await编程 关于C#,可以看看Learning Hard的博客

  6. 二、基于事件的异步编程模式(EAP)

    一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进 ...

  7. 基于任务的异步编程模式,Task-based Asynchronous Pattern

    术语: APM           异步编程模型,Asynchronous Programming Model,其中异步操作由一对 Begin/End 方法(如 FileStream.BeginRea ...

  8. [.net 多线程]异步编程模式

    .NET中的异步编程 - EAP/APM 从.NET 4.5开始,支持的三种异步编程模式: 基于事件的异步编程设计模式 (EAP,Event-based Asynchronous Pattern) 异 ...

  9. 基于任务的异步编程模式(TAP)的错误处理

    在前面讲到了<基于任务的异步编程模式(TAP)>,但是如果调用异步方法,没有等待,那么调用异步方法的线程中使用传统的try/catch块是不能捕获到异步方法中的异常.因为在异步方法执行出现 ...

随机推荐

  1. 008-Java中方法的使用(进阶篇)

    目录 一.方法的重载(overload) 一.什么是方法的重载 二.方法执行时的内存变化 一.JVM主要三块内存空间 二.关于栈的数据结构(如图) 三.方法执行过程内存变化(用以下代码演示) 三.方法 ...

  2. sql指令,增,删,查,改

    增 insert into table (name,sex,age) value('张三','男','20')   向表中的name,sex,age,分别添加张三,男,20的内容 查 select  ...

  3. scrapy爬虫框架调用百度地图api数据存入数据库

    scrapy安装配置不在本文 提及, 1.在开始爬取之前,必须创建一个新的Scrapy项目.进入自定义的项目目录中,运行下列命令 scrapy startproject mySpider 其中, my ...

  4. python3使用迭代生成器yield减少内存占用

    技术背景 在python编码中for循环处理任务时,会将所有的待遍历参量加载到内存中.其实这本没有必要,因为这些参量很有可能是一次性使用的,甚至很多场景下这些参量是不需要同时存储在内存中的,这时候就会 ...

  5. LinqToObject和LinqToSql的区别

    抓住五一假期尾巴和小伙伴们一起分享这两者的区别.大家在日常编码的过程当中肯定也注意过或者使用过.但是二者其实存在本质的区别 1.什么是LinqToObject呢? LINQ to Objects指直接 ...

  6. yapi简介,安装及使用

    简介 ​ YApi 是高效.易用.功能强大的 api 管理平台,旨在为开发.产品.测试人员提供更优雅的接口管理服务.可以帮助开发者轻松创建.发布.维护 API,YApi 还为用户提供了优秀的交互体验, ...

  7. POJ3040给奶牛发工资

    题意:       有n种硬币,每种硬币有mi个,然后让你给奶牛发工资,每周发至少c元(就是不找零钱的意思)然后问你能发几周?(硬币之间都是倍数关系) 思路:       这个题目做了两天,丢脸,看完 ...

  8. LA3135简单多路归并(优先队列)

    题意:       有N个任务,每个任务都有自己的时间间隔(就是每t秒请求执行一次)和任务id,这n个任务公用一个cpu,每次我们都执行时间靠前的,如果相同时间内有多个任务,就执行任务id小的,要求模 ...

  9. WSL2+Ubuntu配置Java Maven Hadoop Spark环境

    所需文件: 更新日期为2021/5/8: Linux 内核更新包 JDK1.8 maven3.8.1 hadoop3.3.0 spark3.1.1 WSL?WSL2? WSL是适用于 Linux 的 ...

  10. PowerDesigner安装教程

    准备安装 准备好可执行文件.汉化.破解文件 将可执行文件以管理员身份运行: 向导初始化 next 选香港.点同意: 可以默认,如果说这个路径不能用下次就换一个 除了Eclipse全选 全选 好像有个空 ...