2023年6月22日发(作者:)
C#多线程委托⽅式让主界⾯显⽰传值内容c# delegate(委托)与多线程很多时候写windows程序都需要结合多线程,在.net中⽤如下得代码来创建并启动⼀个新的线程。public void ThreadProc();Thread thread = new Thread( new ThreadStart( ThreadProc ) );ground = true;();但是很多时候,在新的线程中,我们需要与UI进⾏交互,在.net中不允许我们直接这样做。可以参考MSDN中的描述:“Windows 窗体”使⽤单线程单元 (STA) 模型,因为“Windows 窗体”基于本机 Win32 窗⼝,⽽ Win32 窗⼝从本质上⽽⾔是单元线程。STA 模型意味着可以在任何线程上创建窗⼝,但窗⼝⼀旦创建后就不能切换线程,并且对它的所有函数调⽤都必须在其创建线程上发⽣。除了 Windows 窗体之外,.NET Framework中的类使⽤⾃由线程模型。STA 模型要求需从控件的⾮创建线程调⽤的控件上的任何⽅法必须被封送到(在其上执⾏)该控件的创建线程。基类 Control 为此⽬的提供了若⼲⽅法(Invoke、BeginInvoke 和 EndInvoke)。Invoke⽣成同步⽅法调⽤;BeginInvoke ⽣成异步⽅法调⽤。Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另⼀个线程调⽤控件的⽅法,那么必须使⽤控件的⼀个Invoke ⽅法来将调⽤封送到适当的线程。正如所看到的,我们必须调⽤Invoke⽅法,⽽BeginInvoke可以认为是Invoke的异步版本。调⽤⽅法如下:public delegate void OutDelegate(string text);public void OutText(string text){ Text(text); Text( "/t/n" );}OutDelegate outdelegate = new OutDelegate( OutText );nvoke(outdelegate, new object[]{text});如果我们需要在另外⼀个线程⾥⾯对UI进⾏操作,我们需要⼀个类似OutText的函数,还需要⼀个该函数的委托delegate,当然,这⾥展⽰的是⾃定义的,.net中还有很多其他类型的委托,可以直接使⽤,不需要⽽外声明。例如:MethodInvoker和EventHandler,这两种类型委托的函数外观是固定的,MethodInvoker是void Function()类型的委托,⽽EventHandler是void Function(object, EventArgs)类型的委托,第⼀个不⽀持参数,第⼆中的参数类型和数量都是固定的,这两种委托可以很⽅便的调⽤,但是缺乏灵活性。请注意BeginInvoke前⾯的对象是this,也就是主线程。现在再介绍Required,Control是所有控件的基类,对于这个属性MSDN的描述是:获取⼀个值,该值指⽰调⽤⽅在对控件进⾏⽅法调⽤时是否必须调⽤ Invoke ⽅法,因为调⽤⽅位于创建控件所在的线程以外的线程中。该属性可⽤于确定是否必须调⽤ Invoke ⽅法,当不知道什么线程拥有控件时这很有⽤。也就是说通过判断InvokeRequired可以知道是否需要⽤委托来调⽤当前控件的⼀些⽅法,如此可以把OutText函数修改⼀下:public delegate void OutDelegate(string text);public void OutText(string text){ if( Required ) { OutDelegate outdelegate = new OutDelegate( OutText ); nvoke(outdelegate, new object[]{text}); return; } Text(text); Text( "/t/n" );}注意,这⾥的函数没有返回,如果有返回,需要调⽤Invoke或者EndInvoke来获得返回的结果,不要因为包装⽽丢失了返回值。如果调⽤没有完成,Invoke和EndInvoke都将会引起阻塞。
现在如果我有⼀个线程函数如下:public void ThreadProc(){ for(int i = 0; i < 5; i++) { OutText( ng() ); (1000); }}如果循环的次数很⼤,或者漏了(1000);,那么你的UI肯定会停⽌响应,想知道原因吗?看看BeginInvoke前⾯的对象,没错,就是this,也就是主线程,当你的主线程不停的调⽤OutText的时候,UI当然会停⽌响应。与以前VC中创建⼀个新的线程需要调⽤AfxBeginThread函数,该函数中第⼀个参数就是线程函数的地址,⽽第⼆个参数是⼀个类型为LPVOID的指针类型,这个参数将传递给线程函数。现在我们没有办法再使⽤这种⽅法来传递参数了。我们需要将传递给线程的参数和线程函数包装成⼀个单独的类,然后在这个类的构造函数中初始化该线程所需的参数,然后再将该实例的线程函数传递给Thread类的构造函数。代码⼤致如下:public class ProcClass{ private string procParameter = ""; public ProcClass(string parameter) { procParameter = parameter; } public void ThreadProc() { }}ProcClass threadProc = new ProcClass("use thread class");Thread thread = new Thread( new ThreadStart( Proc ) );ground = true;();就是这样,需要建⽴⼀个中间类来传递线程所需的参数。那么如果我的线程⼜需要参数,⼜需要和UI进⾏交互的时候该怎么办呢?可以修改⼀下代码:public class ProcClass{ private string procParameter = ""; private egate delg = null; public ProcClass(string parameter, egate delg) { procParameter = parameter; = delg; } public void ThreadProc() { nvoke("use Proc()", null, null); }}ProcClass threadProc = new ProcClass("use thread class", new OutDelegate(OutText));Thread thread = new Thread( new ThreadStart( Proc ) );ground = true;();
发布者:admin,转转请注明出处:http://www.yc00.com/news/1687424115a9030.html
评论列表(0条)