.Net的异步机制(APM核心:IAsyncResult) - 下篇

异步的核心: IAsyncResult

Asynchronous Programming Model

 

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

IAsyncResult接口

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

AsyncResult类

public class AsyncResult : IAsyncResult, IMessageSink
    {
        //IAsyncResult 的实现      
        public virtual WaitHandle AsyncWaitHandle { get; }
        public virtual bool IsCompleted { get; }
        public virtual object AsyncState { get; }
        public virtual bool CompletedSynchronously { get; }

        // 其他一些重要的属性
        public bool EndInvokeCalled { get; set; } //检验是否调用了EndInvoke()
        public virtual object AsyncDelegate { get; } //获取原始的委托对象,可查看上一篇文章中的Code 4.1/4.2/5
    }

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

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

Code 1:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);

            AsyncTest test = new AsyncTest();
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
            //使用回调函数
            AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
            IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);

            DoAntherJob();
            Console.ReadLine(); // 让黑屏等待,不会直接关闭..
        }

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

        static void OnSalaryCallback(IAsyncResult asyncResult)
        {
            //通过AsyncState 获取额外的参数.
            decimal para = (int)asyncResult.AsyncState;

            //通过AsyncDelegate 获取原始的委托对象
            AsyncResult obj = (AsyncResult)asyncResult;
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;

            if (asyncResult.IsCompleted)// 判读是否已经调用完成
                Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);

            decimal val = del.EndInvoke(asyncResult);

            Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
        }
    }

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

图1

我们看到DoAntherJob 比异步YearlySalary快2秒,看代码中(注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(100000, 15, 100000, 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 = 
32 (MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
33 
34             if (asyncResult.IsCompleted)// 判读是否已经调用完成
35                 Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
36 
37             decimal val = del.EndInvoke(asyncResult);
38             Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
39         }
40 
41         static void OnHelloCallback(IAsyncResult asyncResult)
42         {
43             //通过AsyncDelegate 获取原始的委托对象
44             AsyncResult obj = (AsyncResult)asyncResult;
45             MyThirdAsyncCode.AsyncTest.AsyncEventHandler del = 
46 (MyThirdAsyncCode.AsyncTest.AsyncEventHandler)obj.AsyncDelegate;
47 
48             if (asyncResult.IsCompleted)// 判读是否已经调用完成
49                 Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
50 
51             string val = del.EndInvoke(asyncResult);
52             Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
53         }
54     }
55 
56     public class AsyncTest
57     {
58         public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
59         public delegate string AsyncEventHandler(string name); // 对应Hello 方法
60         public string Hello(string name)
61         {
62             //模拟耗时/复杂的逻辑计算.等待5秒
63             Thread.Sleep(5000);
64             return "Hello:" + name;
65         }
66         public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
67         {
68             //模拟耗时/复杂的逻辑计算.
69             Thread.Sleep(3000);//等待3秒
70             return salary * monthCount + bonus;
71         }
72     }
View Code

图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:

static void OnSalaryCallback(IAsyncResult asyncResult)
        {
            //通过AsyncDelegate 获取原始的委托对象
            AsyncResult obj = (AsyncResult)asyncResult;
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;

            decimal val = del.EndInvoke(asyncResult);
            asyncResult.AsyncWaitHandle.Close();//显示的释放资源
        }

当开始调用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 != null) throw m_exception;
63         }
64 
65         Implementation of IAsyncResult
66     }
View Code

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

Code3.2

internal class AsyncResult<TResult> : AsyncResultNoResult
    {
        // Field set when operation completes
        private TResult m_result = default(TResult);

        public AsyncResult(AsyncCallback asyncCallback, Object state) :
            base(asyncCallback, state) { }

        public void SetAsCompleted(TResult result,
           Boolean completedSynchronously)
        {
            // Save the asynchronous operation's result
            m_result = result;

            // Tell the base class that the operation completed 
            // sucessfully (no exception)
            base.SetAsCompleted(null, completedSynchronously);
        }

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

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

Code3.3:

internal sealed class LongTask
    {
        private Int32 m_ms;  // Milliseconds;

        public LongTask(Int32 seconds)
        {
            m_ms = seconds * 1000;
        }

        // Synchronous version of time-consuming method
        public DateTime DoTask()
        {
            Thread.Sleep(m_ms);  // Simulate time-consuming task
            return DateTime.Now; // Indicate when task completed
        }

        // Asynchronous version of time-consuming method (Begin part)
        public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
        {
            // Create IAsyncResult object identifying the 
            // asynchronous operation
            AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
               callback, state);

            // Use a thread pool thread to perform the operation
            ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);

            return ar;  // Return the IAsyncResult to the caller
        }

        // Asynchronous version of time-consuming method (End part)
        public DateTime EndDoTask(IAsyncResult asyncResult)
        {
            // We know that the IAsyncResult is really an 
            // AsyncResult<DateTime> object
            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;

            // Wait for operation to complete, then return result or 
            // throw exception
            return ar.EndInvoke();
        }

        // Asynchronous version of time-consuming method (private part 
        // to set completion result/exception)
        private void DoTaskHelper(Object asyncResult)
        {
            // We know that it's really an AsyncResult<DateTime> object
            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
            try
            {
                // Perform the operation; if sucessful set the result
                DateTime dt = DoTask();
                ar.SetAsCompleted(dt, false);
            }
            catch (Exception e)
            {
                // If operation fails, set the exception
                ar.SetAsCompleted(e, false);
            }
        }
    }

引用自 :http://www.cnblogs.com/30ErLi/archive/2010/09/19/1830729.html

转载于:https://www.cnblogs.com/wangquan0816/p/3193074.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值