zoukankan      html  css  js  c++  java
  • .Net Core自实现CLR异步编程模式(Asynchronous programming patterns)

    最近在看一个线程框架,对.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关键字会将代码分为同步和回调,这个模式的实现还是需要反编译源码去知道编译器做了哪些动作。以后有时间我会和大家探讨一下这其中的原理。

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

  • 相关阅读:
    thinkPHP入门之二
    thinkPHP入门
    斐波那契数列,冒泡排序,选择排序,数组去重
    jquery-懒加载插件
    本地存储之cookie
    javascript的快速排序法
    [luogu2165 AHOI2009] 飞行棋 (枚举)
    [luogu2576 SCOI2010] 幸运数字 (容斥原理)
    [luogu2587 ZJOI2008] 泡泡堂 (贪心)
    [luogu2602 ZJOI2010] 数字计数 (数位dp)
  • 原文地址:https://www.cnblogs.com/neilhu/p/14844332.html
Copyright © 2011-2022 走看看