zoukankan      html  css  js  c++  java
  • 什么是.Net的异步机制(APM核心:IAsyncResult) step 3

    异步的核心: IAsyncResult

    Asynchronous Programming Model

     

    整个异步调用过程中都是围绕IAsyncResult来进行的,大家可以看看上篇文章的例子,BeginXXX 返回这个对象,EndXXX接收这个对象来结束当前异步对象,下面我们来看看IAsyncResult接口成员/和实现此接口的AsyncResult成员(其中有些在上篇中已经涉及到)

    IAsyncResult接口

    复制代码
    1public interface IAsyncResult
    2    {
    3        WaitHandle AsyncWaitHandle get; } //阻塞一个线程,直到一个或多个同步对象接收到信号
    4        Boolean IsCompleted get; } //判读当前异步是否完成
    5        Object AsyncState get; } //获取额外的参数值,请看上一篇文章的Code 4.3
    6        Boolean CompletedSynchronously get; } //几乎没有使用
    7    }
    复制代码

    AsyncResult

    复制代码
     1    public class AsyncResult : IAsyncResult, IMessageSink
     2    {
     3        //IAsyncResult 的实现      
     4        public virtual WaitHandle AsyncWaitHandle get; }
     5        public virtual bool IsCompleted get; }
     6        public virtual object AsyncState get; }
     7        public virtual bool CompletedSynchronously get; }
     8
     9        // 其他一些重要的属性
    10        public bool EndInvokeCalled getset; } //检验是否调用了EndInvoke()
    11        public virtual object AsyncDelegate get; } //获取原始的委托对象,可查看上一篇文章中的Code 4.1/4.2/5
    12    }
    复制代码

     

    注意:基本上都是只读属性

    下面我们来看看异步的执行顺序,并回顾下 IAsyncResult 下各个属性的应用,如果还是不熟悉请看前2篇文章.

    Code 1:

    复制代码
     1   class Program
     2    {
     3        static void Main(string[] args)
     4        {
     5            Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
     6
     7            AsyncTest test = new AsyncTest();
     8            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
     9            //使用回调函数
    10            AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
    11            IAsyncResult ar = del.BeginInvoke(10000015100000, callback, 2000);
    12
    13            DoAntherJob();
    14            Console.ReadLine(); // 让黑屏等待,不会直接关闭..
    15        }

    16
    17        //开始其他工作.
    18        static void DoAntherJob()
    19        {
    20            Thread.Sleep(1000);//需要1秒才能完成这个工作,注1
    21            Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
    22        }

    23
    24        static void OnSalaryCallback(IAsyncResult asyncResult)
    25        {
    26            //通过AsyncState 获取额外的参数.
    27            decimal para = (int)asyncResult.AsyncState;
    28
    29            //通过AsyncDelegate 获取原始的委托对象
    30            AsyncResult obj = (AsyncResult)asyncResult;
    31            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
    (MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
    32
    33            if (asyncResult.IsCompleted)// 判读是否已经调用完成
    34                Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
    35
    36            decimal val = del.EndInvoke(asyncResult);
    37
    38            Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
    39        }

    40    }

    41
    42    public class AsyncTest
    43    {
    44        public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
    45        public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
    46        {
    47            //模拟耗时/复杂的逻辑计算.
    48            Thread.Sleep(3000);//等待3秒,注2
    49            return salary * monthCount + bonus;
    50        }

    51    }
    复制代码



    1

    我们看到DoAntherJob 比异步YearlySalary2,看代码中(1)(2),两个线程的执行结果

    接下来,我们说说AsyncWaitHandle 属性. 他返回WaitHandle对象(System.Threading.WaitHandle), 他有3个重要的方法. WaitOne / WaitAny / WaitAll ,我们先来说下WaitOne,Code1代码基础上只是增加了下面红色部分.

    1,WaitOne

    Code 1.1

    IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
    //
    阻碍当前线程,直到异步调用结束.
    ar.AsyncWaitHandle.WaitOne();

    //
    开始其他工作.
    DoAntherJob();


    1.1

    执行输出,对比图1我们可以看到执行的次序不一样了(看时间),调用WaitOne,会阻碍当前线程,直到异步完成,才释放当前的线程, WaitOne 提供了时间的重载版本WaitOne(int millisecondsTimeout)/ WaitOne(TimeSpan timeout);来判断阻碍的时间.无参的版本是无限等待的(直到异步调用结束)

    2, WaitAll

    我们在Code1的代码基础上加上Hello的异步调用(使Main提供多个异步调用),注意红色部分.

    Code 1.2

    复制代码
     1    class Program
     2    {
     3        static void Main(string[] args)
     4        {
     5            Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
     6
     7            AsyncTest test = new AsyncTest();
     8            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
     9            MyThirdAsyncCode.AsyncTest.AsyncEventHandler asy = test.Hello;
    10
    11            IAsyncResult salayAsyc = del.BeginInvoke(10000015100000, OnSalaryCallback, null);
    12            IAsyncResult helloAsyc = asy.BeginInvoke("Hello Andy", OnHelloCallback, null);
    13            //把所有异步的句柄保存到WaitHandle 对象中
    14            WaitHandle[] handles = { salayAsyc.AsyncWaitHandle, helloAsyc.AsyncWaitHandle };
    15            //阻碍当前线程,直到所有异步调用结束.
    16            WaitHandle.WaitAll(handles);
    17
    18            //开始其他工作.
    19            DoAntherJob();
    20            Console.ReadLine(); // 让黑屏等待,不会直接关闭..
    21        }

    22        static void DoAntherJob()
    23        {
    24            Thread.Sleep(1000);//需要1秒才能完成这个工作,注1
    25            Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
    26        }

    27        static void OnSalaryCallback(IAsyncResult asyncResult)
    28        {
    29            //通过AsyncDelegate 获取原始的委托对象
    30            AsyncResult obj = (AsyncResult)asyncResult;
    31            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
    (MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
    32
    33            if (asyncResult.IsCompleted)// 判读是否已经调用完成
    34                Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
    35
    36            decimal val = del.EndInvoke(asyncResult);
    37            Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
    38        }

    39
    40        static void OnHelloCallback(IAsyncResult asyncResult)
    41        {
    42            //通过AsyncDelegate 获取原始的委托对象
    43            AsyncResult obj = (AsyncResult)asyncResult;
    44            MyThirdAsyncCode.AsyncTest.AsyncEventHandler del = 
    (MyThirdAsyncCode.AsyncTest.AsyncEventHandler)obj.AsyncDelegate;
    45
    46            if (asyncResult.IsCompleted)// 判读是否已经调用完成
    47                Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
    48
    49            string val = del.EndInvoke(asyncResult);
    50            Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
    51        }

    52    }

    53
    54    public class AsyncTest
    55    {
    56        public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
    57        public delegate string AsyncEventHandler(string name); // 对应Hello 方法
    58        public string Hello(string name)
    59        {
    60            //模拟耗时/复杂的逻辑计算.等待5秒
    61            Thread.Sleep(5000);
    62            return "Hello:" + name;
    63        }

    64        public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
    65        {
    66            //模拟耗时/复杂的逻辑计算.
    67            Thread.Sleep(3000);//等待3秒
    68            return salary * monthCount + bonus;
    69        }

    70    }
    复制代码



    1.2

    从图1.2中可以看出,WaitAll会阻碍当前线程(主线程#10),等待所有异步的对象都执行完毕(耗时最长的异步),才释放当前的线程,WaitAll/WaitAny的重载版本和WaitOne一样.

    3, WaitAny

    WaitAll 基本上是一样的.我们可以使用 WaitAny 来指定某个/某几个委托先等待,修改Code1.2红色部分,使用WaitAny.

    Code1.3
    //
    salayAsyc异步的句柄保存到WaitHandle 对象中
    WaitHandle
    [] handles = { salayAsyc.AsyncWaitHandle };
    //
    阻碍当前线程,直到所有异步调用结束.
    WaitHandle
    .WaitAny(handles);

    1.3

    我们阻碍了DoAntherJob(#10)线程,直到Salary异步调用计算完成.同样我们可以巧用这三个方法来改变我们方法执行的顺序.

     

    释放资源

    Code2

    复制代码
    1static void OnSalaryCallback(IAsyncResult asyncResult)
    2        {
    3            //通过AsyncDelegate 获取原始的委托对象
    4            AsyncResult obj = (AsyncResult)asyncResult;
    5            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
    (MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
    6
    7            decimal val = del.EndInvoke(asyncResult);
    8            asyncResult.AsyncWaitHandle.Close();//显示的释放资源
    9        }
    复制代码

     

    当开始调用BeginXXX,就会创建一个新的AsyncResult对象.这个对象会构造一个WaitHandle句柄(通过AsyncWaitHandle访问),当我们EndXXX,并不会马上关闭这个句柄,而是等待垃圾收集器来关闭,这时候我们最后在调用EndXXX完成后,显示的关闭这个句柄.

     

    说到这里,我们基本上把异步方法都解释一遍,下面我们来看看重构的异步对象,我们也可以细细体会异步对象的内部执行代码..下面Code3.1/3.2/3.3代码来自Jeffery Richard大师的Power Threading类库,具体可查看http://msdn.microsoft.com/en-us/magazine/cc163467.aspx

     

    重构的异步对象

     

    1,构造一个内部无参的AsyncResultNoResult对象,继承IAsyncResult接口(保留原创的注释)

    Code3.1

    复制代码
      1 internal class AsyncResultNoResult : IAsyncResult
      2    {
      3        // Fields set at construction which never change while 
      4        // operation is pending
      5        private readonly AsyncCallback m_AsyncCallback;
      6        private readonly Object m_AsyncState;
      7
      8        // Fields set at construction which do change after 
      9        // operation completes
     10        private const Int32 c_StatePending = 0
     11        private const Int32 c_StateCompletedSynchronously = 1;
     12        private const Int32 c_StateCompletedAsynchronously = 2;
     13        private Int32 m_CompletedState = c_StatePending;
     14
     15        // Field that may or may not get set depending on usage
     16        private ManualResetEvent m_AsyncWaitHandle;
     17
     18        // Fields set when operation completes
     19        private Exception m_exception;
     20
     21        public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
     22        {
     23            m_AsyncCallback = asyncCallback;
     24            m_AsyncState = state;
     25        }

     26
     27        public void SetAsCompleted(
     28           Exception exception, Boolean completedSynchronously)
     29        {
     30            // Passing null for exception means no error occurred. 
     31            // This is the common case
     32            m_exception = exception;
     33
     34            // The m_CompletedState field MUST be set prior calling the callback
     35            Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
     36               completedSynchronously ? c_StateCompletedSynchronously :
     37               c_StateCompletedAsynchronously);
     38            if (prevState != c_StatePending)
     39                throw new InvalidOperationException(
     40                    "You can set a result only once");
     41
     42            // If the event exists, set it
     43            if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
     44
     45            // If a callback method was set, call it
     46            if (m_AsyncCallback != null) m_AsyncCallback(this);
     47        }

     48
     49        public void EndInvoke()
     50        {
     51            // This method assumes that only 1 thread calls EndInvoke 
     52            // for this object
     53            if (!IsCompleted)
     54            {
     55                // If the operation isn't done, wait for it
     56                AsyncWaitHandle.WaitOne();
     57                AsyncWaitHandle.Close();
     58                m_AsyncWaitHandle = null;  // Allow early GC
     59            }

     60
     61            // Operation is done: if an exception occured, throw it
     62            if (m_exception != nullthrow m_exception;
     63        }

     64
     65        Implementation of IAsyncResult
    115    }
    复制代码

     

    2,继承AsyncResultNoResult对象,并且为他提供返回值和泛型的访问

    Code3.2

    复制代码
     1internal class AsyncResult<TResult> : AsyncResultNoResult
     2    {
     3        // Field set when operation completes
     4        private TResult m_result = default(TResult);
     5
     6        public AsyncResult(AsyncCallback asyncCallback, Object state) :
     7            base(asyncCallback, state) { }
     8
     9        public void SetAsCompleted(TResult result,
    10           Boolean completedSynchronously)
    11        {
    12            // Save the asynchronous operation's result
    13            m_result = result;
    14
    15            // Tell the base class that the operation completed 
    16            // sucessfully (no exception)
    17            base.SetAsCompleted(null, completedSynchronously);
    18        }

    19
    20        new public TResult EndInvoke()
    21        {
    22            base.EndInvoke(); // Wait until operation has completed 
    23            return m_result;  // Return the result (if above didn't throw)
    24        }

    25    }
    复制代码


    3,模拟长时间的异步工作

    Code3.3

    复制代码
     1internal sealed class LongTask
     2    {
     3        private Int32 m_ms;  // Milliseconds;
     4
     5        public LongTask(Int32 seconds)
     6        {
     7            m_ms = seconds * 1000;
     8        }

     9
    10        // Synchronous version of time-consuming method
    11        public DateTime DoTask()
    12        {
    13            Thread.Sleep(m_ms);  // Simulate time-consuming task
    14            return DateTime.Now; // Indicate when task completed
    15        }

    16
    17        // Asynchronous version of time-consuming method (Begin part)
    18        public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
    19        {
    20            // Create IAsyncResult object identifying the 
    21            // asynchronous operation
    22            AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
    23               callback, state);
    24
    25            // Use a thread pool thread to perform the operation
    26            ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);
    27
    28            return ar;  // Return the IAsyncResult to the caller
    29        }

    30
    31        // Asynchronous version of time-consuming method (End part)
    32        public DateTime EndDoTask(IAsyncResult asyncResult)
    33        {
    34            // We know that the IAsyncResult is really an 
    35            // AsyncResult<DateTime> object
    36            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
    37
    38            // Wait for operation to complete, then return result or 
    39            // throw exception
    40            return ar.EndInvoke();
    41        }

    42
    43        // Asynchronous version of time-consuming method (private part 
    44        // to set completion result/exception)
    45        private void DoTaskHelper(Object asyncResult)
    46        {
    47            // We know that it's really an AsyncResult<DateTime> object
    48            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
    49            try
    50            {
    51                // Perform the operation; if sucessful set the result
    52                DateTime dt = DoTask();
    53                ar.SetAsCompleted(dt, false);
    54            }

    55            catch (Exception e)
    56            {
    57                // If operation fails, set the exception
    58                ar.SetAsCompleted(e, false);
    59            }

    60        }

    61    }
    复制代码


    来自
    Jeffrey Richter大师更多更详细的异步操作方法, 请查看http://www.wintellect.com/PowerThreading.aspx,对于一些朋友可能看不懂Code3.1-3.3代码(其实没什么所谓的),因为涉及到过多的线程知识,这里出于让你获得更多的更深层次的(异步)认识为目的,才提供上面代码,在以后的文章会再次探讨.

     

    下一篇章中,我们来看看微软提供有异步调用的类是如何调用的,并从中我会给出些真实应用环境中的一些小技巧,让你编写的代码更健壮更完善.

    以上有word 文档直接粘贴,排版可能不太看,你可以通过下面来下载相应的代码/文档

    1,文档

    2,代码(VS2008开发,.Net Framework 2.0(C Sharp)编写)

  • 相关阅读:
    springboot2.0整合es的异常总结
    redis的主从模式搭建及注意事项
    一个可配置的爬虫采集系统的方案实现
    初识 go 语言:方法,接口及并发
    初识 go 语言:数据类型
    Science论文"Clustering by fast search and find of density peaks"学习笔记
    OpenCV和Matplotlib色彩空间模式不一致的问题
    Using OpenCV Java with Eclipse
    KMeans聚类算法Hadoop实现
    Ubuntu环境变量——添加与删除
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/3105403.html
Copyright © 2011-2022 走看看