.net利用委托BeginInvoke和EndInvoke实现异步编程
最近看书,看到了可以利用学过的委托知识实现异步编程,这里做一个简单的说明示例。如果委托对象在调用列表中只有一个方法(引用方法),他就可有异步执行这个方法。委托类有两个方法,BeginInvoke和EndInvoke。
- 当我们调用委托的BeginInvoke方法时,它开始在一个独立线程上执行引用方法。并且立即返回到原始线程。原始线程可以继续。而引用方法会在线程池中的线程中并行执行。
- 当程序希望获取已完成的异步方法的结果时,可以检查BeginInvoke返回的IAsyncResult的IsCompleted属性。或调用委托的EndInvoke方法来等待委托完成。
等待-直到完成 模式
delegate string MyDownLoadDel(string weburl);
static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb);
IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", null, null);
Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
string str = del.EndInvoke(iar);
Debug.WriteLine(string.Format("EndInvoke,下载网站大小:{0}耗时:{1,4:N0}ms", str.Length, watch.Elapsed.TotalMilliseconds));
Console.ReadKey(); //暂停
}
public static string DownLoadWeb(string url)
{
WebClient wc = new WebClient();
string str = wc.DownloadString(url);
Debug.WriteLine(string.Format("下载网站{0}结束", url));
return str;
}
返回的结果为:
开始计时: 0ms
委托BeginInvoke: 33ms
下载网站https://www.asp.net/结束
EndInvoke,下载网站大小:30543耗时:1,025ms
轮询模式
delegate string MyDownLoadDel(string weburl);
static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb);
IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", null, null);
Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
while (!iar.IsCompleted)
{
Debug.WriteLine(string.Format("还没有完成:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
Thread.Sleep(1000);
}
string str = del.EndInvoke(iar);
Debug.WriteLine(string.Format("EndInvoke,下载网站大小:{0}耗时:{1,4:N0}ms", str.Length, watch.Elapsed.TotalMilliseconds));
Console.ReadKey(); //暂停
}
public static string DownLoadWeb(string url)
{
WebClient wc = new WebClient();
string str = wc.DownloadString(url);
Debug.WriteLine(string.Format("下载网站{0}结束", url));
return str;
}
返回结果为:
开始计时: 0ms
委托BeginInvoke: 29ms
还没有完成: 29ms
还没有完成:1,052ms
还没有完成:2,059ms
还没有完成:3,066ms
还没有完成:4,078ms
下载网站https://www.asp.net/结束
EndInvoke,下载网站大小:30561耗时:5,084ms
回调模式:
回调模式和前面的不同之处在于,一旦初始线程发起了异步方法,它就自己管自己,不在考虑同步。当异步方法调用结束之后,系统调用一个用户自定义的方法来处理结果。并且返回委托的EndInvoke方法。这个用户自定义方法称为回调或者回调函数。
BeginInvoke的参数列表中最后两个额外的参数由回调函数使用。
- 第一个参数callback,是回调方法的名字。
- 第二个参数state,可以是null或传入回调方法的一个对象的引用。我们可以通过使用IAsyncResult参数的AsyncState属性来获取这个对象,参数的类型是object。
delegate string MyDownLoadDel(string weburl);
static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb);
IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", new AsyncCallback(CallWhenDone), null);
//new AsyncCallback 表示异步操作完成调用,如果直接写CallWhenDone,那么程序运行是同步完成
Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
Thread.Sleep(1000);
Debug.WriteLine(string.Format("主线程运行耗时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
Console.ReadKey(); //暂停
}
public static string DownLoadWeb(string url)
{
WebClient wc = new WebClient();
string str = wc.DownloadString(url);
Debug.WriteLine(string.Format("下载网站{0}结束", url));
return str;
}
public static void CallWhenDone(IAsyncResult iar)
{
Debug.WriteLine(string.Format("进入回调方法体"));
AsyncResult ar = (AsyncResult)iar; //获取类对象的引用
MyDownLoadDel del = (MyDownLoadDel)ar.AsyncDelegate; //获取委托的引用
string str = del.EndInvoke(iar); //调用EndInvoke
Debug.WriteLine(string.Format("回调方法获取结果为Length:{0}", str.Length));
}
结果如下:
开始计时: 0ms
委托BeginInvoke: 26ms
主线程运行耗时:1,029ms
下载网站https://www.asp.net/结束
进入回调方法体
回调方法获取结果为Length:30543
修改BeginInvoke的第二个state参数,我们传入del,可以直接用iar.AsyncState在回调函数中获取委托的引用。代码如下:
delegate string MyDownLoadDel(string weburl);
static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb);
IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", new AsyncCallback(CallWhenDone), del);
//new AsyncCallback 表示异步操作完成调用,如果直接写CallWhenDone,那么程序运行是同步完成
Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
Thread.Sleep(1000);
Debug.WriteLine(string.Format("主线程运行耗时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds));
Console.ReadKey(); //暂停
}
public static string DownLoadWeb(string url)
{
WebClient wc = new WebClient();
string str = wc.DownloadString(url);
Debug.WriteLine(string.Format("下载网站{0}结束", url));
return str;
}
public static void CallWhenDone(IAsyncResult iar)
{
Debug.WriteLine(string.Format("进入回调方法体"));
MyDownLoadDel del = (MyDownLoadDel)iar.AsyncState; //获取委托的引用
string str = del.EndInvoke(iar); //调用EndInvoke
Debug.WriteLine(string.Format("回调方法获取结果为Length:{0}", str.Length));
}
结果如下:
开始计时: 0ms
委托BeginInvoke: 26ms
主线程运行耗时:1,027ms
下载网站https://www.asp.net/结束
进入回调方法体
回调方法获取结果为Length:30561
可以看到主线程的运行没有影响,是在执行完DownLoadWeb方法后才会仅仅回调方法CallWhenDone中。
参考文档:https://msdn.microsoft.com/zh-cn/library/system.iasyncresult(v=vs.110).aspx