异步的核心: 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
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
图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
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
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