通过轮询方式,使用IsCompleted属性判断异步操作是否完成,这样在异步操作未完成前就可以让主线程执行另外的工作。代码如下:
class Program { delegate string MyDelegate(string name); static void Main(string[] args) { ThreadMessage("Main Thread"); //建立委托 MyDelegate myDelegate = new MyDelegate(Hello); //异步调用委托,获取计算结果 IAsyncResult result = myDelegate.BeginInvoke("Leslie", null, null); //在异步线程未完成前执行其他工作 while (!result.IsCompleted) { Thread.Sleep(200); //虚拟操作 Console.WriteLine("Main thead do work!"); } //下列 AsyncWaitHandle.WaitOne(int timeout) 可达到相同效果 //while (!result.AsyncWaitHandle.WaitOne(200)) //{ // Console.WriteLine("Main thead do work!"); //} //此处可加入多个检测对象, while (!result.IsCompleted) ,while (!result2.IsCompleted)...也可以判断多个 //WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ }; //WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle}; //while (!WaitHandle.WaitAll(waitHandleList,200)) //{ // Console.WriteLine("Main thead do work!"); //}
string data = myDelegate.EndInvoke(result); Console.WriteLine(data); Console.ReadKey(); } static string Hello(string name) { ThreadMessage("Async Thread"); Thread.Sleep(2000); return "Hello " + name; } static void ThreadMessage(string data) { string message = string.Format("{0}\n ThreadId is:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }
上诉情况好比,硬件中的 总线 被占用的情况,如果外设需要请求总线,那么需要不断的询问 是否别人使用完毕,或者我是否可以申请了。
这种方式,现实中比较贴切,但是对于追求高效率的硬件设备,过于频繁的请求可不是特别的好。使用轮询方式来检测异步方法的状态
非常麻烦,而且效率不高,有见及此,.NET为 IAsyncResult BeginInvoke(AsyncCallback , object)准备了一个回调函数。使用
AsyncCallback 就可以绑定一个方法作为回调函数,回调函数必须是带参数 IAsyncResult 且无返回值的方法:
void AsycnCallbackMethod(IAsyncResult result) 。在BeginInvoke方法完成后,系统就会调用AsyncCallback所绑定的回调函数
,最后回调函数中调用 XXX EndInvoke(IAsyncResult result) 就可以结束异步方法,它的返回值类型与委托的返回值一致。
如下代码:利用回调函数实现异步多线程调用,也是在现实中比较实用的
class Program { public class Person { public string Name; public int Age; } delegate string MyDelegate(string name); static void Main(string[] args) { ThreadMessage("主线程开始!"); //建立委托 MyDelegate myDelegate = new MyDelegate(Hello); //建立Person对象 Person person = new Person(); person.Name = "Elva"; person.Age = 27; //异步调用委托,输入参数对象person, 获取计算结果 myDelegate.BeginInvoke("Leslie", new AsyncCallback(Completed), person); //在启动异步线程后,主线程可以继续工作而不需要等待 for (int n = 0; n < 6; n++) { Console.WriteLine(" 主线程运行!"); Thread.Sleep(2000); //为了看到异步线程结束后主线程还在工作,sleep } Console.WriteLine(" 主线程运行!"); Console.WriteLine(""); Console.ReadKey(); } static string Hello(string name) { ThreadMessage("异步线程运行!"); Thread.Sleep(2000); return "\nHello " + name; } static void Completed(IAsyncResult result) //可以用该返回返回 { ThreadMessage("异步线程完成"); //获取委托对象,调用EndInvoke方法获取运行结果 AsyncResult _result = (AsyncResult)result; MyDelegate myDelegate = (MyDelegate)_result.AsyncDelegate; string data = myDelegate.EndInvoke(_result); //获取Person对象 Person person = (Person)result.AsyncState; string message = person.Name + "'s age is " + person.Age.ToString(); Console.WriteLine(data + "\n" + message); } static void ThreadMessage(string data) { string message = string.Format("{0}\n 当前线程ID为:{1}", data, Thread.CurrentThread.ManagedThreadId); Console.WriteLine(message); } }
主线在调用BeginInvoke方法可以继续执行其他命令,而无需再等待了,这无疑比使用轮询方式判断异步方法是否完成更有优势。
在异步方法执行完成后将会调用AsyncCallback所绑定的回调函数,注意一点,回调函数依然是在异步线程中执行,这样就不会影响主线程的运行,
在异步方法执行完成后将会调用AsyncCallback所绑定的回调函数,注意一点,回调函数依然是在异步线程中执行,这样就不会影响主线程的运行,
这也使用回调函数最值得青昧的地方。
在回调函数中有一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就可以通过 AsyncResult.AsyncDelegate 获取原委托,
在回调函数中有一个既定的参数IAsyncResult,把IAsyncResult强制转换为AsyncResult后,就可以通过 AsyncResult.AsyncDelegate 获取原委托,
再使用EndInvoke方法获取计算结果。