黄聪:C#多线程教程(1):BeginInvoke和EndInvoke⽅法,解
决主线程延时。。。
开发语⾔:C#3.0
IDE:Visual Studio 2008
本系列教程主要包括如下内容:
1.  BeginInvoke和EndInvoke⽅法
2.  Thread类
3. 线程池
4. 线程同步基础
5. 死锁
6. 线程同步的7种⽅法
7. 如何在线程中访问GUI组件
⼀、线程概述
在操作系统中⼀个进程⾄少要包含⼀个线程,然后,在某些时候需要在同⼀个进程中同时执⾏多项任务,或是为了提供程序的性能,将要执⾏的任务分解成多个⼦任务执⾏。这就需要在同⼀个进程中开启多个线程。我们使⽤C#编写⼀个应⽤程序(控制台或桌⾯程序都可以),然后运⾏这个程序,并打开windows任务管理器,这时我们就会看到这个应⽤程序中所含有的线程数,如下图所⽰。
如果任务管理器没有“线程数”列,可以【查看】>【选择列】来显⽰“线程计数”列。从上图可以看出,⼏乎所有的进程都拥有两个以上的线程。从⽽可以看出,线程是提供应⽤程序性能的重要⼿段之⼀,尤其在多核CPU的机器上尤为明显。
⼆、⽤委托(Delegate)的BeginInvoke和EndInvoke⽅法操作线程
在C#中使⽤线程的⽅法很多,使⽤委托的BeginInvoke和EndInvoke⽅法就是其中之⼀。BeginInvoke⽅法可以使⽤线程异步地执⾏委托所指向的⽅法。然后通过EndInvoke⽅法获得⽅法的返回值(EndInvoke⽅法的返回值就是被调⽤⽅法的返回值),或是确定⽅法已经被成功调⽤。我们可以通过四种⽅法从EndInvoke⽅法来获得返回值。
三、直接使⽤EndInvoke⽅法来获得返回值
当使⽤BeginInvoke异步调⽤⽅法时,如果⽅法未执⾏完,EndInvoke⽅法就会⼀直阻塞,直到被调⽤的⽅法执⾏完毕。如下⾯的代码所⽰:
在运⾏上⾯的程序后,由于newTask⽅法通过Sleep延迟了2秒,因此,程序直到2秒后才输出最终结果(⼀个随机整数)。如果不调
writeline教程⽤EndInvoke⽅法,程序会⽴即退出,这是由于使⽤BeginInvoke创建的线程都是后台线程,这种线程⼀但所有的前台线程都退出后(其中主线程就是⼀个前台线程),不管后台线程是否执⾏完毕,都会结束线程,并退出程序。关于前台和后台线程的详细内容,将在后⾯的部分讲解。
读者可以使⽤上⾯的程序做以下实验。⾸先在Main⽅法的开始部分加⼊如下代码:
Thread.Sleep(10000);
以使Main⽅法延迟10秒钟再执⾏下⾯的代码,然后按Ctrl+F5运⾏程序,并打开企业管理器,观察当前
程序的线程数,假设线程数是4,在10秒后,线程数会增⾄5,这是因为调⽤BeginInvoke⽅法时会建⽴⼀个线程来异步执⾏newTask⽅法,因此,线程会增加⼀个。
四、使⽤IAsyncResult asyncResult属性来判断异步调⽤是否完成
虽然上⾯的⽅法可以很好地实现异步调⽤,但是当调⽤EndInvoke⽅法获得调⽤结果时,整个程序就象死了⼀样,这样做⽤户的感觉并不会太好,因此,我们可以使⽤asyncResult来判断异步调⽤是否完成,并显⽰⼀些提⽰信息。这样做可以增加⽤户体验。代码如下:
上⾯代码的执⾏结果如下图所⽰。
由于是异步,所以“*”可能会在“任务开始”前输出,如上图所⽰。
五、使⽤WaitOne⽅法等待异步⽅法执⾏完成
使⽤WaitOne⽅法是另外⼀种判断异步调⽤是否完成的⽅法。代码如下:
WaitOne的第⼀个参数表⽰要等待的毫秒数,在指定时间之内,WaitOne⽅法将⼀直等待,直到异步调⽤完成,并发出通知,WaitOne⽅法才返回true。当等待指定时间之后,异步调⽤仍未完成,WaitOne⽅法返回false,如果指定时间为0,表⽰不等待,如果为-1,表⽰永远等待,直到异步调⽤完成。
六、使⽤回调⽅式返回结果
上⾯介绍的⼏种⽅法实际上只相当于⼀种⽅法。这些⽅法虽然可以成功返回结果,也可以给⽤户⼀些提⽰,但在这个过程中,整个程序就象死了⼀样(如果读者在GUI程序中使⽤这些⽅法就会⾮常明显),要想在调⽤的过程中,程序仍然可以正常做其它的⼯作,就必须使⽤异步调⽤的⽅式。下⾯我们使⽤GUI程序来编写⼀个例⼦,代码如下:
要注意的是,这⾥使⽤了BeginInvoke⽅法的最后两个参数(如果被调⽤的⽅法含有参数的话,这些参数将作为BeginInvoke的前⾯⼀部分参数,如果没有参数,BeginInvoke就只有两个参数了)。第⼀个参数是回调⽅法委托类型,这个委托只有⼀个参数,就
是IAsyncResult,如MethodCompleted⽅法所⽰。当method⽅法执⾏完后,系统会⾃动调⽤MethodCompleted⽅法。BeginInvoke的第⼆个参数需要向MethodCompleted⽅法中传递⼀些值,⼀般可以传递被调⽤⽅法的委托,如上⾯代码中的my。这个值可以使
⽤IAsyncResult.AsyncState属性获得。
由于上⾯的代码通过异步的⽅式访问的form上的⼀个textbox,因此,需要按ctrl+f5运⾏程序(不能直接按F5运⾏程序,否则⽆法在其他线程中访问这个textbox,关于如果在其他线程中访问GUI组件,并在后⾯的部分详细介绍)。并在form上放⼀些其他的可视控件,然在点
击button1后,其它的控件仍然可以使⽤,就象什么事都没有发⽣过⼀样,在10秒后,在textbox1中将输出100。
七、其他组件的BeginXXX和EndXXX⽅法
在其他的组件中也有类似BeginInvoke和EndInvoke的⽅法,如System.Net.HttpWebRequest类
的BeginGetResponse和EndGetResponse⽅法,下⾯是使⽤这两个⽅法的⼀个例⼦:
原⽂出处:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。