[C#基础]c#中的BeginInvoke和EndEndInvoke
摘要
异步这东西,真正用起来的时候,发现事情还是挺多的,最近在项目中用到了异步的知识,发现对它还是不了解,处理起来,走了不少弯路。觉得还是补一补还是很有必要的。
MSDN原文地址:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx
正文
.Net framework可以让你异步调用任何方法。为达这样的目的,你可以定义一个与你要调用的方法的签名相同的委托。公共语言运行时将自动为该委托定义与签名相同的BeginInvok和EndInvoke方法。
异步委托调用BeginInvok和EndInvoke方法,但在.NET Compact Framework中并不支持。
BeginInvoke方法触发你的异步方法,它和你想要执行的异步方法有相同的参数。另外还有两个可选参数,第一个是AsyncCallback委托是异步完成的回调方法。第二个是用户自定义对象,该对象将传递到回调方法中。BeginInvoke立即返回并且不等待完成异步的调用(继续执行该下面的代码,不需要等待)。BeginInvoke返回IAsyncResult接口,可用于检测异步调用的过程。
通过EndInvoke方法检测异步调用的结果。如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成。EndInvoke参数包括out和ref参数。
下面代码演示使用BeginInvoke和EndInvoke进行异步调用的四种常见方式。在调用BeginInvoke可以做以下工作:
- 做一些其他操作,然后调用EndInvoke方法阻塞线程直到该方法完成。
- 使用IAsyncResult.AsyncWaitHandle属性,使用它的WaitOne方法阻塞线程直到收到WaitHandle信号,然后调用EndInvoke。
- 检查BeginInvoke返回值IAsyncResult的状态来决定方法是否完成,然后调用EndInvoke方法。
- 通过在BeginInvoke方法中传递该委托,在回调方法中调用该委托的EenInvoke方法。
注意
无论你怎么使用,都必须调用EndInvoke方法结束你的异步调用。
下面通过模拟一个耗时的操作,实现上面说的那四种情况。
情况一:通过EndInovke阻塞线程,直到异步调用结束。
using System;
using System.Diagnostics;
using System.Threading; namespace BeginInvokeDemo
{
/// <summary>
/// 委托必须和要调用的异步方法有相同的签名
/// </summary>
/// <param name="callDuration">sleep时间</param>
/// <param name="threadId">当前线程id</param>
/// <returns></returns>
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
class Program
{ /// <summary>
/// 主函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
int threadid = ;
//开启异步操作
IAsyncResult result = caller.BeginInvoke(, out threadid, null, null);
for (int i = ; i < ; i++)
{
Console.WriteLine("其它业务" + i.ToString());
}
//调用EndInvoke,等待异步执行完成
Console.WriteLine("等待异步方法TestMethodAsync执行完成");
string res = caller.EndInvoke(out threadid, result);
Console.WriteLine("Completed!");
Console.WriteLine(res);
Console.Read();
}
/// <summary>
/// 与委托对应的方法
/// </summary>
/// <param name="callDuration"></param>
/// <param name="threadId"></param>
/// <returns></returns>
static string TestMethodAsync(int callDuration, out int threadId)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine("异步TestMethodAsync开始");
for (int i = ; i < ; i++)
{ // 模拟耗时操作
Thread.Sleep(callDuration);
Console.WriteLine("TestMethodAsync:" + i.ToString());
}
sw.Stop();
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
}
}
}
结果

由上图,可以看出,在BeginInvoke开启异步执行方法,会先执行其他的业务。通过EndInvoke方法,阻塞直到异步执行完毕,才会执行EndInvoke之后的代码。
情况二:通过WaitHandle属性阻塞线程。
你可以获得BeginInvoke的返回值的WaitHandle,并使用它的AsyncWaitHanlde属性。WaitHandle信号异步完成时,你可以通过调用WaitOne方法等待。
如果你使用WaitHandle,你可以在之前或者异步调用完成后进行其他的操作。但之后必须使用EndInvoke检查结果。
注意
当你调用EndInvoke方法时,等待句柄并不会自动关闭。如果你释放等待处理的所有引用,当垃圾回收等待句柄是,系统资源将被释放。一旦你完成使用等待句柄,通过WaitHandle的close方法,一次性显示关闭,这时的垃圾回收效率更高。
using System;
using System.Diagnostics;
using System.Threading; namespace BeginInvokeDemo
{
/// <summary>
/// 委托必须和要调用的异步方法有相同的签名
/// </summary>
/// <param name="callDuration">sleep时间</param>
/// <param name="threadId">当前线程id</param>
/// <returns></returns>
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
class Program
{ /// <summary>
/// 主函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
int threadid = ;
//开启异步操作
IAsyncResult result = caller.BeginInvoke(, out threadid, null, null);
for (int i = ; i < ; i++)
{
Console.WriteLine("其它业务" + i.ToString());
}
//调用EndInvoke,等待异步执行完成
Console.WriteLine("等待异步方法TestMethodAsync执行完成");
//等待异步执行完毕信号
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("收到WaitHandle信号");
//通过EndInvoke检查结果
string res = caller.EndInvoke(out threadid, result);
//显示关闭句柄
result.AsyncWaitHandle.Close();
Console.WriteLine("关闭了WaitHandle句柄");
Console.WriteLine("Completed!");
Console.WriteLine(res);
Console.Read();
}
/// <summary>
/// 与委托对应的方法
/// </summary>
/// <param name="callDuration"></param>
/// <param name="threadId"></param>
/// <returns></returns>
static string TestMethodAsync(int callDuration, out int threadId)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine("异步TestMethodAsync开始");
for (int i = ; i < ; i++)
{ // 模拟耗时操作
Thread.Sleep(callDuration);
Console.WriteLine("TestMethodAsync:" + i.ToString());
}
sw.Stop();
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
}
}
}
输出

情况三:检查BeginInvoke返回结果的状态。
可以通过BeginInvoke的返回结果的IsCompleted属性检查异步是否完成。你可以在异步没有完成的时候做其他的操作。
using System;
using System.Diagnostics;
using System.Threading; namespace BeginInvokeDemo
{
/// <summary>
/// 委托必须和要调用的异步方法有相同的签名
/// </summary>
/// <param name="callDuration">sleep时间</param>
/// <param name="threadId">当前线程id</param>
/// <returns></returns>
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
class Program
{ /// <summary>
/// 主函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
int threadid = ;
//开启异步操作
IAsyncResult result = caller.BeginInvoke(, out threadid, null, null);
for (int i = ; i < ; i++)
{
Console.WriteLine("其它业务" + i.ToString());
}
//调用EndInvoke,等待异步执行完成
Console.WriteLine("等待异步方法TestMethodAsync执行完成");
//等待异步执行完毕信号
//result.AsyncWaitHandle.WaitOne();
//Console.WriteLine("收到WaitHandle信号");
//通过循环不停的检查异步运行状态
while (result.IsCompleted==false)
{
Thread.Sleep();
Console.WriteLine("异步方法,running........");
}
//异步结束,拿到运行结果
string res = caller.EndInvoke(out threadid, result);
//显示关闭句柄
result.AsyncWaitHandle.Close();
Console.WriteLine("关闭了WaitHandle句柄");
Console.WriteLine("Completed!");
Console.WriteLine(res);
Console.Read();
}
/// <summary>
/// 与委托对应的方法
/// </summary>
/// <param name="callDuration"></param>
/// <param name="threadId"></param>
/// <returns></returns>
static string TestMethodAsync(int callDuration, out int threadId)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine("异步TestMethodAsync开始");
for (int i = ; i < ; i++)
{ // 模拟耗时操作
Thread.Sleep(callDuration);
Console.WriteLine("TestMethodAsync:" + i.ToString());
}
sw.Stop();
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
}
}
}
输出
其它业务0
其它业务1
其它业务2
其它业务3
其它业务4
其它业务5
其它业务6
其它业务7
其它业务8
其它业务9
等待异步方法TestMethodAsync执行完成
异步TestMethodAsync开始
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
TestMethodAsync:
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
TestMethodAsync:
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
TestMethodAsync:
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
TestMethodAsync:
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
异步方法,running........
TestMethodAsync:
异步方法,running........
关闭了WaitHandle句柄
Completed!
耗时5031ms.
情况四:通过在回调方法中。
如果需要在异步完成后需要做一些其他的操作,你可以在异步完成时执行一个回调方法。在该回调方法中做处理。
首先需要定义一个回调方法。
using System;
using System.Diagnostics;
using System.Threading; namespace BeginInvokeDemo
{
/// <summary>
/// 委托必须和要调用的异步方法有相同的签名
/// </summary>
/// <param name="callDuration">sleep时间</param>
/// <param name="threadId">当前线程id</param>
/// <returns></returns>
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
class Program
{ /// <summary>
/// 主函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
int threadid = ;
//开启异步操作
IAsyncResult result = caller.BeginInvoke(, out threadid, callBackMethod, caller);
for (int i = ; i < ; i++)
{
Console.WriteLine("其它业务" + i.ToString());
}
//调用EndInvoke,等待异步执行完成
Console.WriteLine("等待异步方法TestMethodAsync执行完成");
//等待异步执行完毕信号
//result.AsyncWaitHandle.WaitOne();
//Console.WriteLine("收到WaitHandle信号");
//通过循环不停的检查异步运行状态
//while (result.IsCompleted==false)
//{
// Thread.Sleep(100);
// Console.WriteLine("异步方法,running........");
//}
//异步结束,拿到运行结果
//string res = caller.EndInvoke(out threadid, result);
////显示关闭句柄
//result.AsyncWaitHandle.Close();
Console.WriteLine("关闭了WaitHandle句柄"); //Console.WriteLine(res);
Console.Read();
}
/// <summary>
/// 异步方法回调方法,异步执行完毕,会回调该方法
/// </summary>
/// <param name="ar"></param>
private static void callBackMethod(IAsyncResult ar)
{
AsyncMethodCaller caller = ar.AsyncState as AsyncMethodCaller;
string result = caller.EndInvoke(out int threadid, ar);
Console.WriteLine("Completed!");
Console.WriteLine(result); } /// <summary>
/// 与委托对应的方法
/// </summary>
/// <param name="callDuration"></param>
/// <param name="threadId"></param>
/// <returns></returns>
static string TestMethodAsync(int callDuration, out int threadId)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine("异步TestMethodAsync开始");
for (int i = ; i < ; i++)
{ // 模拟耗时操作
Thread.Sleep(callDuration);
Console.WriteLine("TestMethodAsync:" + i.ToString());
}
sw.Stop();
threadId = Thread.CurrentThread.ManagedThreadId;
return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
}
}
}

总结
在项目中遇到的有参数又返回值的情况,如何在回调方法中拿到委托和传递的参数,当时卡这里了,后来查看情况四的情况,才得以解决。这里也再学习一下。
[C#基础]c#中的BeginInvoke和EndEndInvoke的更多相关文章
- C#中的BeginInvoke和EndEndInvoke 异步问题
- java基础---->java中正则表达式二
跟正则表达式相关的类有:Pattern.Matcher和String.今天我们就开始Java中正则表达式的学习. Pattern和Matcher的理解 一.正则表达式的使用方法 一般推荐使用的方式如下 ...
- Teradata基础教程中的数据库试验环境脚本
Teradata基础教程中的数据库表: Customer: 客户信息表 Location: 位置信息表 Employee: 雇员信息表 Job: 工作信息表 Department: 部门表 ...
- Java基础学习中一些词语和语句的使用
在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...
- 计算机基础--Java中int char byte的关系
计算机基础--Java中int char byte的关系 重要:一个汉字占用2byte,Java中用char(0-65535 Unicode16)型字符来存字(直接打印输出的话是字而非数字),当然要用 ...
- Java基础-Java中的堆内存和离堆内存机制
Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.
- Java基础-Java中的内存分配与回收机制
Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.
- Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock)
Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在学习Java的之前,你可能已经听说过读 ...
- Java基础-Java中的并法库之线程池技术
Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.
随机推荐
- 卸载移动硬盘出现 device is busy
umount /dev/sdb1 # device is busy fuser -m -v /dev/sdb1 # 查看 fuser -m -k /dev/sdb1 # 杀死进程
- IE/FF/Chrome下document.documentElement和document.body的 scrollHeight/scrollTop/clientHeight
IEdocument.documentElement.scrollHeight 浏览器所有内容高度 ,document.body.scrollHeight 浏览器所有内容高度document.do ...
- [模板] 2-SAT
昨天早上在准备省队集训,发现自己连2-SAT是什么都不知道,于是一早上都投身于2-SAT模板中,终于有个结果. 思路如下: 1.根据条件表达式建边: 2.缩环: 3.判断是否可行: 4.根据缩完环的图 ...
- VisualSVN Server和Subversion的联系
VisualSVN Server是只能在Windows平台上搭建的SVN服务器,内核使用的是Subversion,做了整合:apache+subversion+WMI(实现操作界面等). 用这个的好处 ...
- Android成长日记-Android监听事件的方法
1. Button鼠标点击的监听事件 --setOnClickListener 2. CheckBox, ToggleButton , RadioGroup的改变事件 --setOnCheckedCh ...
- alertDialog创建登陆界面,判断用户输入
alertDialog创建登陆界面,需要获取用户输入的用户名和密码,获取控件对象的时候不能像主布局文件那样获得, 需要在onClickListener中获取,代码如下: public boolean ...
- BZOJ1088: [SCOI2005]扫雷Mine
这道题A的好莫名其妙啊2333 传送门 状压DP,枚举上一个雷的分布情况(1<<3)-1,然后和当前的分布相结合,推出下一状态. //BZOJ 1088 //by Cydiater //2 ...
- 2-SAT 问题
2-SAT 问题是k-SAT问题在k==2时的特殊情况,因为已经证明k>=3时的k-sat问题属于npc问题.所以在这里仅研究2-SAT的特殊情况. 何为2-sat问题? 简单地说就是有N个 ...
- 高可用与负载均衡(1)之linux系统的数据链路层负载均衡
preface 在蓝厂就职到时候,每台缓存服务器都能够跑到2G的流量,这么大的流量,有人会问,服务器是不是安装的万兆网卡,no no no,仅仅是3张千兆网卡绑定在一块.万兆网卡的服务器少见,大多数都 ...
- UVA11987Almost Union-Find(并查集删除节点)
题目链接 题意:n个数(即1-n)和m个操作: 1表示把x和y合并,2表示把x移到y集合里面,3表示统计x集合的元素个数 1,3好说,关键是2操作,可以先把2删除掉,删除的操作可以找一个其他的数字来取 ...