.NET Framework可以使用委托进行异步编程,亦可使用基于事件的异步模式进行多线程编程。
琢磨了一下使用委托(delegate)进行异步编程。说白了,异步其实就是依靠多线程,用一个线程游离于主线程之外,以执行一个指定任务,而主线程继续做自己的事,任务完成时告诉主线程:"嘿,我完成任务了"。
用到的类和接口也就是:
System.IAsyncResult
System.Runtime.Remoting.Messaging.AsyncResult
System.AsyncCallback(不会显式的运用)
System.Threading.Thread(不会显式的运用)
通过一个例子来说明:
我们需要声明一个委托,届时它将指向一个具有相同签名(所谓签名,我的理解就是参数列表和返回类型)的方法(就是一会我们将要说到的目标方法)。
当调用一个委托时,编译器会生成"Invoke"方法,它会在当前线程(即调用委托的线程)调用目标方法,.NET编译器是支持异步委托的,所以,它将生成"Invoke"方法以及"BeginInvoke"和"EndInvoke"方法。我们需要手动调用BeginInvoke方法。
BeginInvoke的执行过程就是把调用目标方法的请求放入队列,然后立即返回。而目标方法在一个新线程中执行,与主线程并行。BeginInvoke的参数就是目标方法的参数加上另外两个参数,一个是委托类型的参数,指向我们要调用的回调函数(就是目标方法完成以后调用的函数),另一个是Object型参数,是回调方法将要使用的信息的对象。
我们需要一个回调函数,这样目标方法完成时,回调函数将被唤起,起到主动通知的作用。比如我叫你做一件事,我就是主线程,你是新开的线程,我想知道你的事情做完了没有,因为我的一部分工作可能依靠你的工作完成以后才能做,有两个方式:1.我不时的去问你并等着你(我觉的这就不是异步的初衷了)。2.你做完了就告诉我。(2)的方式就是通过回调来让主线程得知。
EndInvoke是在BeginInvoke执行完毕后调用,它是用来得到目标方法执行情况的。主线程可以调,回调函数也可以调。如果主线程调就是我们刚才说的(1),回调函数调就是(2)。我们当然选择(2)。
回调函数怎么调用EndInvoke呢,我们为回调函数安插一个IAsyncResult型参数,通过它可以得到刚在指向目标函数的委托,这样就可以通过这个委托来调用EndInvoke了。BeginInvoke的最后一个参数我们赋予它指向目标函数的委托,这个委托便是刚才说的"回调方法将要使用的信息的对象"。
using System;
using System.Threading;
namespace AysncTest {
public delegate void AsyncDelegate(int number);
public class AsyncTest {
//业务方法
public void businessMethod(int number) {
Console.Out.WriteLine("businessMethod() beging...");
int i;
for (i = 0; i <= number/2; i++) {
Console.Out.WriteLine("businessMethod's count : " + i + " ..thread : " + Thread.CurrentThread.ManagedThreadId);
}
Console.Out.WriteLine("businessMethod() is complete! ");
}
// 回调方法
public void callBackMethod(IAsyncResult ar) {
try {
Console.Out.WriteLine("callBackMethod() beging...");
Console.Out.WriteLine("callBackMethod() @ Thread : " + Thread.CurrentThread.ManagedThreadId);
AsyncDelegate caller = (AsyncDelegate)ar.AsyncState;
for (int i = 0; i <= 100; i++) {
Console.Out.WriteLine("callBackMethod's count : " + i + " ..thread : " + Thread.CurrentThread.ManagedThreadId);
}
caller.EndInvoke(ar);
Console.Out.WriteLine("callBackMethod is complete! ");
} catch (Exception e) {
Console.WriteLine(e.StackTrace);
Console.WriteLine(e.Message);
}
}
//异步调用
public void asyncTestMethod(int number) {
Console.Out.WriteLine("asyncTestMethod() beging...");
Console.Out.WriteLine("asyncTestMethod() @ Thread : " + Thread.CurrentThread.ManagedThreadId);
AsyncDelegate asyncCaller = new AsyncDelegate(this.businessMethod);
AsyncCallback callBack = new AsyncCallback(this.callBackMethod);
IAsyncResult result = asyncCaller.BeginInvoke(number, callBack, asyncCaller);
Console.Out.WriteLine("asyncTestMethod() is complete! ");
}
public static void Main() {
Console.Out.WriteLine("Main() beging...");
int number = 1000;
AsyncTest test = new AsyncTest();
test.asyncTestMethod(number);
for (int i = 0; i <= number; i++) {
Console.Out.WriteLine("Main's count : " + i + " ..thread : " + Thread.CurrentThread.ManagedThreadId);
}
Console.Out.WriteLine("Main() is complete! ");
}
}
}
执行结果:
Main() beging...
asyncTestMethod() beging...
asyncTestMethod() @ Thread : 1
asyncTestMethod() is complete!
Main's count : 0 ..thread : 1
Main's count : 1 ..thread : 1
Main's count : 2 ..thread : 1
businessMethod() beging...
businessMethod's count : 0 ..thread : 3
businessMethod's count : 1 ..thread : 3
. .
. .
. .
businessMethod's count : 385 ..thread : 3
businessMethod's count : 386 ..thread : 3
Main's count : 3 ..thread : 1
Main's count : 4 ..thread : 1
. .
. .
. .
Main's count : 393 ..thread : 1
Main's count : 394 ..thread : 1
businessMethod's count : 387 ..thread : 3
businessMethod's count : 388 ..thread : 3
. .
. .
. .
businessMethod's count : 499 ..thread : 3
businessMethod's count : 500 ..thread : 3
businessMethod() is complete!
callBackMethod() beging...
callBackMethod() @ Thread : 3
callBackMethod's count : 0 ..thread : 3
callBackMethod's count : 1 ..thread : 3
. .
. .
. .
callBackMethod's count : 99 ..thread : 3
callBackMethod's count : 100 ..thread : 3
callBackMethod is complete!
Main's count : 395 ..thread : 1
Main's count : 396 ..thread : 1
. .
. .
. .
Main's count : 999 ..thread : 1
Main's count : 1000 ..thread : 1
Main() is complete!
输出是在控制台上,可以看到主线程和目标方法的输出交替出现,说明是并行的,分别在两个线程上,还可以看到回调函数的执行也在目标方法的线程上。
(黑色:主线程输出; 红色:目标方法输出; 绿色:回调方法输出)

No comments:
Post a Comment