C# 异步委托 BeginInvoke EndInvoke

1. 简单主线程中委托:

static void Main(string[] args)
{
	//定义一个委托,并初始化
	Func<int, int, string> delFunc = (a, b) => (a + b).ToString();//黄色底纹部分换成{ return (a + b).ToString(); }更好理解
	//同步方法调用(跟调用一个方法一样),即是主线程执行这个委托
	string str = delFunc(1, 2);//以往做法
	Console.WriteLine(str);
	Console.ReadKey();
}

执行结果

2. 异步委托:

static void Main(string[] args)
{
	Console.WriteLine("主线程id:"+Thread.CurrentThread.ManagedThreadId);
	//定义一个委托,并初始化
	Func<int, int, string> delFunc = (a, b) =>
	{
//由于下面执行这个委托时使用了BeginInvoke方法,所以就为开启一个新线程去执行,所以称为异步线程
		Console.WriteLine("异步线程id:" + Thread.CurrentThread.ManagedThreadId);
//Thread.Sleep(3000);
		return (a + b).ToString();
	};
	//同步方法调用,即是主线程执行这个委托
	//string str = delFunc(1, 2);
	//BeginInvoke() 方法用于异步委托的执行开始。有返回值,返回值为IAsyncResult,并不是执行委托方法的返回值
	//BeginInvoke() 是可以接受多个参数的,它的参数个数和参数类型取决于定义委托时的参数个数和类型,无论它有多少个参数,最后两个参数都是不变的。
	倒数第二个参数为回调函数,暂用null,
	//最后一个参数是给回调函数传入参数的参数,暂用null
	//IAsyncResult result = delFunc.BeginInvoke(1, 2, null, null);
	delFunc.BeginInvoke(1, 2, null, null);
	//接下来我们看看是不是开启一个新的线程来执行这个委托,因此在最上面先打印出主线程 Console.WriteLine("主线程id:"+Thread.CurrentThread.ManagedThreadId);,改进下委托的初始化
	Console.WriteLine();
	Console.ReadKey();
}

输出: 

从上面的执行结果就可以看出,是两个不同的线程在执行。所以称之为异步委托。也就是说使用委托的BeginInvoke方法,本质就是使用了一个线程池的线程去执行委托指向的方法。不是用主线程去执行。

3. 如何拿到异步委托的结果呢?

	//如何拿到异步委托的结果呢,即返回值,这也就是利用异步委托的优势哦,因为手动写线程只能执行没有返回值的委托(ThreadStart、ParameterizedThreadStart),定义如下:
	public delegate void ThreadStart()
	public delegate void ParameterizedThreadStart(object obj) 
	要执行有返回值的委托,就需要使用异步委托执行
	//1.先拿到BeginInvoke方法的返回值result
	IAsyncResult result = delFunc.BeginInvoke(1, 2, null, null);
	//result.IsCompleted通过这个可以判断异步委托是否执行完成,执行完成返回true
	//if (!result.IsCompleted)
	//{
	  //异步委托没有执行完成做点其他事情
	//}
	没有执行完,主线程就一直执行下面的循环体
	//while (!result.IsCompleted)
	//{
	//    Thread.Sleep(100);
	//    Console.WriteLine("Main thread working...");
	//    Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
	//    Console.WriteLine();
	//}
	//2.调用委托的EndInvoke方法,把BeginInvoke方法的返回值result传入,即可拿到委托方法的执行结果
	string str = delFunc.EndInvoke(result);
	Console.WriteLine(str);
	
	IAsyncResult.IsCompleted 用于监视异步委托的执行状态(true / false),这里的时间是不定的,也就是说一定要等到异步委托执行完成之后,这个属性才会返回 true。通过一个循环,委托方法没有执行完成之前,让主线程做点其他事情,如下:
	while (!result.IsCompleted)
	{
		Thread.Sleep(100);
		Console.WriteLine("Main thread working...");
		Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString());
		Console.WriteLine();
	}

这样,如果异步委托的方法耗时较长,那么主线程会一直工作(循环执行)下去。

在委托方法中加上Thread.Sleep(3000);语句上面绿色底纹所示再次运行发现运行结果要等待一会才显示出来这说明EndInvoke()方法会阻塞当前线程直到异步委托方法执行完成之后才能继续往下执行

BeginInvoke()方法无法转到定义查看,因为它是编译生成时自动生成的方法, 是可以接受多个参数的,它的参数个数和参数类型取决于定义委托时的参数个数和类型,不过无论它有多少个参数,最后两个参数都是不变的。

上面是简单方式的异步委托。

还可以用下面的方法 WaitOne(),自定义一个等待的时间,如果在这个等待时间内异步委托没有执行完成,那么就会执行 while 里面的主线程的逻辑,反之就不会执行。

while (!result.AsyncWaitHandle.WaitOne(1000))//等1秒,1秒后异步委托没有执行完成才执行循环体
{
	Thread.Sleep(100);
	Console.WriteLine("Main thread working...");
	Console.WriteLine("Main thread ID is:" + 
    Thread.CurrentThread.ManagedThreadId.ToString());
	Console.WriteLine();
}

4. 异步委托(带回调函数):

    下面看有回调函数的异步委托。

#region 有回调函数的异步委托
// //BeginInvoke方法倒数第二个参数为回调函数,
//最后一个参数传给回调函数的参数
//AsyncCallback
//第3个参数为AsyncCallback类型,转到定义发现也是一个委托,如下
//public delegate void AsyncCallback(IAsyncResult ar);
//据此我们就可以根据该委托定义一个回调函数,见下
delFunc.BeginInvoke(1, 2, MyAsyncCallback,"123");
#endregion
public static void MyAsyncCallback(IAsyncResult ar)
{
	Console.WriteLine("执行回调函数线程的ID:"+Thread.CurrentThread.ManagedThreadId);
}
	

输出:

       从结果可以看出,异步线程id与回调函数的ID是同一个。因为执行BeginInvoke方法时到线程池中取出一个有效线程去执行委托delFunc,然后该线程又去执行回调函数。执行过程如下图所示。

 

//回调函数是干什么用的?什么时候会被执行呢?
//回调函数:是异步委托方法执行完成之后,再来调回调函数,也就是说异步委托方法执行之后,还需要处理些事情就可以用回调函数来处理。

//1.在回调函数中如何拿到异步委托执行的结果(前面讲过靠调用委托的EndInvoke方法,即可拿到委托执行的结果,所以关键是如何拿到异步委托)
public static void MyAsyncCallback(IAsyncResult ar)
{
	   //1.1把回调函数的参数ar强转为实例类型
	AsyncResult result = (AsyncResult)ar;
	//1.2通过实例类型的AsyncDelegate属性拿到异步委托(实际就是delFunc),然后再强转为它的实际类型
	var del=(Func<int, int, string>)result.AsyncDelegate;
	//1.3通过del的EndInvoke方法,参数为ar的实例类型或者就为ar,就可以得到异步委托执行结果
	string returnvalue = del.EndInvoke(ar);
	Console.WriteLine(returnvalue);

	//2.如何拿到给回调函数的参数:利用ar.AsyncState或result.AsyncState属性
	Console.WriteLine("传给回调函数的参数:"+ar.AsyncState);
	//总结:以上拿到了异步委托的执行结果和传给回调函数的参数,在后续就可以做些其他处理。
当然还有更简单些的方法拿到异步委托执行结果和给回调函数的参数。办法是改造前面的BeginInvoke方法最后一个参数,设置为当前的异步委托(因为最后一个参数为object类型,所以可改为任何类型)
	//delFunc.BeginInvoke(1, 2, MyAsyncCallback, delFunc);
	//这样的话ar.AsyncState获取的就是异步委托
   var del= (Func<int, int, string>)ar.AsyncState;
	//接下来要获取返回值只要如下语句即可获取到执行异步委托的返回值
   string str= del.EndInvoke(ar);
	Console.WriteLine(str);

	Console.WriteLine("回调函数的ID:"+Thread.CurrentThread.ManagedThreadId);
}

 

 

 

本文根据徐照兴教授讲义编写,有些改动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值