今天来总结下C#委托的异步调用的基础运用。
单线程(同步)委托调用
namespace DelegateDemo
{
class Program
{
public delegate int IntOp(int x, int y);
public static int Add(int x, int y)
{
Console.WriteLine("Add()所在线程ID{0}",Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);//模拟耗时的操作
return x + y;
}
static void Main(string[] args)
{
Console.WriteLine("Main()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var dg = new IntOp(Add);
int x = 3, y = 4;
int rs = dg(x,y);
//直到Add()执行完毕,此处才开始执行
DoOtherJob();
Console.WriteLine("{0}+{1}={2}", x, y,rs);
Console.ReadLine();
}
public static void DoOtherJob()
{
Console.WriteLine("DoOtherJob()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
}
}
}
这段程序有一个问题,当Add()方法在执行一系列的耗时操作时,在Add()方法之后执行的方法都不得不挂起等待。那么,我们能不能让委托在单独的线程上调用方法,以便模拟“同时”运行任务呢?
关键点:(C#编译器为IntOp委托动态生成的类定义的方法)
- public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);
- public int EndInvoke(IAsyncResult result);
我们修改Main()方法如下:
static void Main(string[] args)
{
Console.WriteLine("Main()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var dg = new IntOp(Add);
int x = 3, y = 4;
IAsyncResult iar = dg.BeginInvoke(x, y, null, null);
//在主线程做其他事情
DoOtherJob();
var rs = dg.EndInvoke(iar);
Console.WriteLine("{0}+{1}={2}", x, y,rs);
Console.ReadLine();
}
此时Add()方法已经在独立线程上被调用,并不影响主线程上的方法执行。现在,假设我们只想在委托IntOp未调用完成,才执行DoOtherJob()方法,而一旦委托调用完成就去获取结果,我们需要怎么判断异步调用是否真的完成了呢?
关键点:(IAsyncResult接口提供的两个属性)
- IsCompleted
- AsyncWaitHandle
我们修改Main()方法如下:
static void Main(string[] args)
{
Console.WriteLine("Main()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var dg = new IntOp(Add);
int x = 3, y = 4;
IAsyncResult iar = dg.BeginInvoke(x, y, null, null);
//while (!iar.IsCompleted){
//在主线程做其他事情
// DoOtherJob();
// Thread.Sleep(1000);
//}
while (!iar.AsyncWaitHandle.WaitOne(1000,true))
{
//在主线程做其他事情
DoOtherJob();
}
var rs = dg.EndInvoke(iar);
Console.WriteLine("{0}+{1}={2}", x, y,rs);
Console.ReadLine();
}
IsCompleted与AsyncWaitHandle.WaitOne的区别在于后者可以指定一个时间间隔t。WaitOne方法会每隔t时间,阻塞当前进程,直到当前的 WaitHandle 收到完成信号为止,收到信号返回True,否则返回False;所以在以上代码IsCompleted的while循环调用了Thread.Sleep(1000)(否则,会不断得去判断是否完成),而WaitOne没有。
除了通过在主线程不断去轮询来确定异步调用执行是否结束的方法外,我们是不是可以通过让次线程主动通知调用线程的方法呢?
关键点:
- AsyncCallback
namespace DelegateDemo
{
class Program1
{
public delegate int IntOp(int x, int y);
private static bool isDone = false;// 静态变量
public static int Add(int x, int y)
{
Console.WriteLine("Add()所在线程ID{0}",Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(5000);//模拟耗时的操作
return x + y;
}
static void Main(string[] args)
{
Console.WriteLine("Main()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var dg = new IntOp(Add);
int x = 3, y = 4;
IAsyncResult iar = dg.BeginInvoke(x, y, new AsyncCallback(AddComplete), null);
while(!isDone)
{
//在主线程做其他事情
DoOtherJob();
Thread.Sleep(1000);
}
var rs = dg.EndInvoke(iar);
Console.WriteLine("{0}+{1}={2}", x, y,rs);
Console.ReadLine();
}
public static void DoOtherJob()
{
Console.WriteLine("DoOtherJob()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
}
public static void AddComplete(IAsyncResult iar)
{
Console.WriteLine("AddComplete()方法所在线程ID{0}",Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Add()方法已经执行完毕");
isDone = true;
}
}
}
上面的程序修改,我们可能会产生这样的疑问,既然Add()方法已经执行完毕,为什么不在AddComplete方法中直接输出呢,下面我们就来修改AddComplete()方法实现直接输出结果。
首先我们要引用:System.Runtime.Remoting.Messaging
关键点:
- AsyncResult类的 AsyncDelegate属性
修改后的代码如下:
public static void AddComplete(IAsyncResult iar)
{
Console.WriteLine("AddComplete()方法所在线程ID{0}",Thread.CurrentThread.ManagedThreadId);
var ar = (AsyncResult) iar;
var dg = (IntOp)ar.AsyncDelegate;
Console.WriteLine("Add()方法已经执行完毕,执行结果是{0}",dg.EndInvoke(iar));
isDone = true;
}
再有个疑问,假如我不仅仅想出结果,我想把参数x,y也传给AddComplete方法,好让它输出完整的公式呢?
关键点:
- IAsyncResult的AsyncState
- AsyncState是object类型,所以我们可以传递任意对象类型(涉及强制转换)
我们对Main()方法以及AddComplete()方法进行稍微的修改,我们采用List类型来传递参数。
Main()方法修改如下:
static void Main(string[] args)
{
Console.WriteLine("Main()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var dg = new IntOp(Add);
int x = 3, y = 4;
IAsyncResult iar = dg.BeginInvoke(x, y, new AsyncCallback(AddComplete), new List<int>{x,y});
while(!isDone)
{
//在主线程做其他事情
DoOtherJob();
Thread.Sleep(1000);
}
Console.ReadLine();
}
AddComplete()方法修改如下:
public static void AddComplete(IAsyncResult iar)
{
Console.WriteLine("AddComplete()方法所在线程ID{0}", Thread.CurrentThread.ManagedThreadId);
var ar = (AsyncResult)iar;
var dg = (IntOp)ar.AsyncDelegate;
var l =iar.AsyncState as List<int>;
if (l != null && l.Count > 1)//必要的判断
{
Console.WriteLine("{0}+{1}={2}", l[0], l[1], dg.EndInvoke(iar));
}
Console.WriteLine("Add()方法已经执行完毕!");
isDone = true;
}