博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
什么是.Net的异步机制(APM核心:IAsyncResult) - step 3
阅读量:5212 次
发布时间:2019-06-14

本文共 13395 字,大约阅读时间需要 44 分钟。

在上一篇文章(), 我们已经简单介绍了异步的调用方式, 下面我们来看看异步的核心.

异步的核心: IAsyncResult

Asynchronous Programming Model

 

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

IAsyncResult接口

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

AsyncResult

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

 

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

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

Code 1:

 1
   
class
 Program
 2
ExpandedBlockStart.gifContractedBlock.gif    
{
 3        static void Main(string[] args)
 4ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 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()
19ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
25ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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
ExpandedBlockStart.gifContractedBlock.gif    
{
44        public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
45        public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
46ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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
ExpandedBlockStart.gifContractedBlock.gif    
{
 3        static void Main(string[] args)
 4ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 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 对象中
14ExpandedSubBlockStart.gifContractedSubBlock.gif            WaitHandle[] handles = { salayAsyc.AsyncWaitHandle, helloAsyc.AsyncWaitHandle };
15            //阻碍当前线程,直到所有异步调用结束.
16            WaitHandle.WaitAll(handles);
17
18            //开始其他工作.
19            DoAntherJob();
20            Console.ReadLine(); // 让黑屏等待,不会直接关闭..
21        }
22        static void DoAntherJob()
23ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
28ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
41ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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
ExpandedBlockStart.gifContractedBlock.gif    
{
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)
59ExpandedSubBlockStart.gifContractedSubBlock.gif        {
60            //模拟耗时/复杂的逻辑计算.等待5秒
61            Thread.Sleep(5000);
62            return "Hello:" + name;
63        }
64        public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
65ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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

1
static
 
void
 OnSalaryCallback(IAsyncResult asyncResult)
2
ExpandedBlockStart.gifContractedBlock.gif        
{
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类库,具体可查看

 

重构的异步对象

 

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

Code3.1

  1
 
internal
 
class
 AsyncResultNoResult : IAsyncResult
  2
ExpandedBlockStart.gifContractedBlock.gif    
{
  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)
 22ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 23            m_AsyncCallback = asyncCallback;
 24            m_AsyncState = state;
 25        }
 26
 27        public void SetAsCompleted(
 28           Exception exception, Boolean completedSynchronously)
 29ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 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()
 50ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 51            // This method assumes that only 1 thread calls EndInvoke 
 52            // for this object
 53            if (!IsCompleted)
 54ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 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
 65ContractedSubBlock.gifExpandedSubBlockStart.gif        Implementation of IAsyncResult#region Implementation of IAsyncResult
 66ExpandedSubBlockStart.gifContractedSubBlock.gif        public Object AsyncState get return m_AsyncState; } }
 67
 68        public Boolean CompletedSynchronously
 69ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 70            get
 71ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 72                return Thread.VolatileRead(ref m_CompletedState) ==
 73                    c_StateCompletedSynchronously;
 74            }
 75        }
 76
 77        public WaitHandle AsyncWaitHandle
 78ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 79            get
 80ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 81                if (m_AsyncWaitHandle == null)
 82ExpandedSubBlockStart.gifContractedSubBlock.gif                {
 83                    Boolean done = IsCompleted;
 84                    ManualResetEvent mre = new ManualResetEvent(done);
 85                    if (Interlocked.CompareExchange(ref m_AsyncWaitHandle,
 86                       mre, null!= null)
 87ExpandedSubBlockStart.gifContractedSubBlock.gif                    {
 88                        // Another thread created this object's event; dispose 
 89                        // the event we just created
 90                        mre.Close();
 91                    }
 92                    else
 93ExpandedSubBlockStart.gifContractedSubBlock.gif                    {
 94                        if (!done && IsCompleted)
 95ExpandedSubBlockStart.gifContractedSubBlock.gif                        {
 96                            // If the operation wasn't done when we created 
 97                            // the event but now it is done, set the event
 98                            m_AsyncWaitHandle.Set();
 99                        }
100                    }
101                }
102                return m_AsyncWaitHandle;
103            }
104        }
105
106        public Boolean IsCompleted
107ExpandedSubBlockStart.gifContractedSubBlock.gif        {
108            get
109ExpandedSubBlockStart.gifContractedSubBlock.gif            {
110                return Thread.VolatileRead(ref m_CompletedState) !=
111                    c_StatePending;
112            }
113        }
114        #endregion
115    }

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

Code3.2

 1
internal
 
class
 AsyncResult
<
TResult
>
 : AsyncResultNoResult
 2
ExpandedBlockStart.gifContractedBlock.gif    
{
 3        // Field set when operation completes
 4        private TResult m_result = default(TResult);
 5
 6        public AsyncResult(AsyncCallback asyncCallback, Object state) :
 7ExpandedSubBlockStart.gifContractedSubBlock.gif            base(asyncCallback, state) { }
 8
 9        public void SetAsCompleted(TResult result,
10           Boolean completedSynchronously)
11ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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()
21ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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

 1
internal
 
sealed
 
class
 LongTask
 2
ExpandedBlockStart.gifContractedBlock.gif    
{
 3        private Int32 m_ms;  // Milliseconds;
 4
 5        public LongTask(Int32 seconds)
 6ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 7            m_ms = seconds * 1000;
 8        }
 9
10        // Synchronous version of time-consuming method
11        public DateTime DoTask()
12ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
19ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
33ExpandedSubBlockStart.gifContractedSubBlock.gif        {
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)
46ExpandedSubBlockStart.gifContractedSubBlock.gif        {
47            // We know that it's really an AsyncResult<DateTime> object
48            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
49            try
50ExpandedSubBlockStart.gifContractedSubBlock.gif            {
51                // Perform the operation; if sucessful set the result
52                DateTime dt = DoTask();
53                ar.SetAsCompleted(dt, false);
54            }
55            catch (Exception e)
56ExpandedSubBlockStart.gifContractedSubBlock.gif            {
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)编写)

文章为原创,如果需要引用,请保留原地址. 有什么问题/错误的地方请联系 fox7805034 (at) hotmail.com

转载于:https://www.cnblogs.com/AndyHuang/archive/2008/12/31/1366000.html

你可能感兴趣的文章
js 封装获取元素的第一个元素
查看>>
iOS 获取Home键指纹验证
查看>>
Python-Mac 安装 PyQt4
查看>>
P2571 [SCOI2010]传送带
查看>>
哈希表1
查看>>
用Data Url (data:image/jpg;base64,)将小图片生成数据流形式
查看>>
实验2-2
查看>>
C#初识
查看>>
String,StringBuffer与StringBuilder的区别?? .
查看>>
JavaScript(三) 数据类型
查看>>
移动端rem布局屏幕适配插件(放js中便可使用)
查看>>
Docker
查看>>
bzoj2259 [Oibh]新型计算机
查看>>
对位与字节的深度认识
查看>>
C++编程基础二 16-习题4
查看>>
MongoDB遇到的疑似数据丢失的问题。不要用InsertMany!
查看>>
服务器被疑似挖矿程序植入107.174.47.156,发现以及解决过程(建议所有使用sonatype/nexus3镜像的用户清查一下)...
查看>>
类型“XXX”的控件“XXXX”必须放在具有 runat=server 的窗体标记内。
查看>>
JQuery 学习
查看>>
session token两种登陆方式
查看>>