第一步:
对于任何需要进行异步调用的方法,需要定义一个委托。为什么需要这个?按照我的理解是对于异步操作一般是在另外一个线程中执行方法的代码块,而委托所传递的相当于是函数的指针。即将委托传递给异步执行的线程,在异步线程中通过调用委托(函数指针)执行需要异步执行的方法。
第二步:
启动异步操作。在C#中启动异步操作一般都是针对委托而言。因为运行时会为每个委托对象创建与之相对应的BeginInvoke和EndInvoke方法,BeginInvoke方法的输入参数除了具有异步执行的输入参数外还具有两个额外的参数:
第一个参数为AsyncCallback委托public delegate void AsyncCallback(IAsyncResult ar)
,该参数为一个回调委托(相当于是回调函数),当异步调用的方法执行完后会调用回调函数处理后续结果。第二个参数为一个用户定义的对象该参数将信息传递给第一个参数的回调函数使用。BeginInvoke方法会立即返回到调用线程中继续执行后续的代码,同时会返回一个IAsyncResult接口。
IAsyncResult接口说明及使用:
1.IAsyncResult包含的属性:
1.1AsyncState:该属性返回一个对象(BeginInvoke的第二个输入参数即用户定义的对象)。
1.2IsCompleted:获取一个值,该值指示异步操作是否已经完成。
2.IAsyncResult的使用方法:
在理解IAsyncResult的使用方法时,需要知道另外的一个类(AsyncResult类)。AsyncResult 类与使用委托所进行的异步方法调用一起使用。 从该委托的 BeginInvoke 方法返回的 IAsyncResult 可以强制转换为 AsyncResult。 AsyncResult 具有 AsyncDelegate 属性,该属性保存对其调用异步调用的委托对象。在AsyncResult中保存了开始异步调用的委托,这相当的重要,因为如果在AsyncResult中保存了异步委托,即相当于是IAsyncResult中保存了异步委托。那么注意AsyncCallback这个回调函数的输入参数就是IAsyncResult,也就是说可以在回调函数中先将IAsyncResult强制转换成AsyncResult,然后在AsyncResult中通过转换AsyncDelegate 来调用EndInvoke,结束异步调用,代码如下:
直接使用MSDN代码
using System;
using System.Threading;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
Thread.Sleep(callDuration);
threadId = Thread.CurrentThread.ManagedThreadId;
return String.Format("My call time was {0}.", callDuration.ToString());
}
}
// The delegate must have the same signature as the method
// it will call asynchronously.
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}
...
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class AsyncMain
{
static void Main()
{
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
// The threadId parameter of TestMethod is an out parameter, so
// its input value is never used by TestMethod. Therefore, a dummy
// variable can be passed to the BeginInvoke call. If the threadId
// parameter were a ref parameter, it would have to be a class-
// level field so that it could be passed to both BeginInvoke and
// EndInvoke.
int dummy = 0;
// Initiate the asynchronous call, passing three seconds (3000 ms)
// for the callDuration parameter of TestMethod; a dummy variable
// for the out parameter (threadId); the callback delegate; and
// state information that can be retrieved by the callback method.
// In this case, the state information is a string that can be used
// to format a console message.
IAsyncResult result = caller.BeginInvoke(3000,
out dummy,
new AsyncCallback(CallbackMethod),
"The call executed on thread {0}, with return value \"{1}\".");
Console.WriteLine("The main thread {0} continues to execute...",
Thread.CurrentThread.ManagedThreadId);
// The callback is made on a ThreadPool thread. ThreadPool threads
// are background threads, which do not keep the application running
// if the main thread ends. Comment out the next line to demonstrate
// this.
Thread.Sleep(4000);
Console.WriteLine("The main thread ends.");
}
// The callback method must have the same signature as the
// AsyncCallback delegate.
static void CallbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
AsyncResult result = (AsyncResult) ar;
AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;
// Retrieve the format string that was passed as state
// information.
string formatString = (string) ar.AsyncState;
// Define a variable to receive the value of the out parameter.
// If the parameter were ref rather than out then it would have to
// be a class-level field so it could also be passed to BeginInvoke.
int threadId = 0;
// Call EndInvoke to retrieve the results.
string returnValue = caller.EndInvoke(out threadId, ar);
// Use the format string to format the output message.
Console.WriteLine(formatString, threadId, returnValue);
}
}
}
/* This example produces output similar to the following:
The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
*/
第三步:
结束异步操作。结束异步操作使用委托的EndInvoke, EndInvoke 参数包括需要异步执行方法中的out 和 ref 参数(在 Visual Basic 中为 < > ByRef和ByRef)以及由BeginInvoke 返回的 IAsyncResult 。
但是如果异步调用的方法还没有执行完就使用EndInvoke,会阻塞主线程直到异步方法执行完后。由于回调函数的使用是在异步调用方法执行完后开始执行,所以一般是在回调函数中使用EndInvoke结束异步调用。(MSDN原话)若要使用回调方法,必须将表示回调方法的 AsyncCallback 委托传递给 BeginInvoke。 也可以传递包含回调方法要使用的信息的对象。 在回调方法中,可以将 IAsyncResult(回调方法的唯一参数)强制转换为 AsyncResult 对象。 然后,可以使用 AsyncResult.AsyncDelegate 属性获取已用于启动调用的委托,以便可以调用EndInvoke。