最近在看一个线程框架,对.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. 前端进阶(2)使用fetch/axios时, 如何取消http请求

    前端进阶(2)使用fetch/axios时, 如何取消http请求 1. 需求 现在前端都是SPA,我们什么时候需要取消HTTP请求呢? 当我们从一个页面跳转到另外一个页面时,如果前一个页面的请求还没 ...

  2. 14.Quick QML-TextInput详解

    1.TextInput属性用来编辑一行文本,对应QLineEdit,除了正常输入外,我们还可以设置echoMode属性改为密码显示状态,也可以通过validator属性和inputMask属性来设置验 ...

  3. 微服务的进程间通信(IPC)

    微服务的进程间通信(IPC) 目录 微服务的进程间通信(IPC) 术语 概述 通信视角 APIs 消息格式 RPC REST gRPC 断路器 API通信的健壮性 服务发现 异步消息 概念 消息 消息 ...

  4. k8s新手必看

    转载自https://juejin.im/post/6844903840139968520 Kubernetes零基础快速入门!初学者必看! 起源 Kubernetes 源自于 google 内部的服 ...

  5. ajax--实现异步请求,接受响应及执行回调

    ajax最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页的内容 ajax指是一种创建交互式网页应用的网页开发技术,其实就是实现前后端交互. 1)ajax是异步javascr ...

  6. Java IO流知识总结

    Java IO的原理 I/O是Input和Output的缩写,用来处理设备之间的数据传输.如读/写文件,网络通讯等. Java程序中,对于数据的输入/输出操作以"流(stream)" ...

  7. Windows PE 第十二章 PE变形技术

    PE变形技术 这章东西太多,太细了.这里我只记录了一些重点概念.为后面学习做铺垫. PE变形:改变PE结构之后,PE加载器依然可以成功加载运行我们的程序. 一 变形常用技术: 结构重叠技术.空间调整技 ...

  8. c# 通过 p/invoke 使用 c的加密程序 参数传递问题

    最近项目中使用需要上位机和下位机通过rs232通信,涉及到通讯加密问题, 硬件那边主要是pcb layout的,于是我就把加密的活拦了过来,锻炼锻炼 首先说明问题: 在c中,加密解密都测试通过,然后在 ...

  9. Docker+Nginx配置SSL

    参考阿里云文档教程 前提条件 购买服务器的服务商开启443端口和服务器已开启443端口(https的默认端口) nginx容器已经安装http_ssl_module模块(启用SSL功能) 下面的教程基 ...

  10. linux集群压测部署方案

    我们今天主要分享的内容从三方面讲解. 集群压力机部署 shell脚本简介 shell脚本搞定压力机部署 集群压力机部署   linux.png 分布式压测背景介绍 在企业项目实战时,如果被压的服务器处 ...