1、Invoke的用法V 2008精简教程(13):Invoke的用法 2010-06-18 14:44:00| 分类: 软件编程 | 标签:|字号大中小 订阅在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI
2、线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。(以上说明来自网络) using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms; namespace
3、 BKWorkerSample public partial class Form2 : Form public Form2() InitializeComponent(); /声明委托 delegate void InvokeDele(int i); /界面线程函数 void worker_DoWork(int i) listBox1.Items.Add(i.ToString(); private void button1_Click(object sender, EventArgs e) System.Threading.Thread thr = new System.Threading.
4、Thread(new System.Threading.ThreadStart(work); thr.Start(); /线程函数,执行耗时任务 private void work() for (int i = 0; i 10; i+) System.Threading.Thread.Sleep(500); /调用invoke 函数,实现了与UI线程的通讯 this.Invoke(inDele, i); InvokeDele inDele; private void Form2_Load(object sender, EventArgs e) inDele = new InvokeDele(w
5、orker_DoWork); 总结:(1)Invoke 或 begininvoke 实现了任务线程数据与UI线程(主进程)界面控件的数据通讯,提供了一种方法;(2)使用了线程、委托;(3)可分两部分: a. 线程:声明线程(启动线程);实现线程函数,在线程函数中调用 this.Invoke(delegate, project ); b. 委托:声明委托类型 delegate void InvokeDele(int i); 实例化委托 InvokeDele inDele; inDele = new InvokeDele(worker_DoWork); 实现委托函数void worker_DoWo
6、rk(int i) ;(4)相对BackGroundWorker, 要灵活些,但同时也麻烦些。V 2008精简教程(12):BackGroundWorker用法 2010-06-18 14:07:16| 分类: 软件编程 | 标签:|字号大中小 订阅为什么要用BackGroundWorker?(1)BackgroundWorker 是最简单的、典型的异步线程应用。启用的线程不用UI线程维护。(2)一般情况下,一个任务必须要有其他线程执行,但是还需要和UI线程交互,在执行线程中,不能直接调用UI线程的控件赋值 方法。这时候 用BackgroundWorker 是最简单的方法。什么是BackGro
7、undWorker? BackgroundWorker是.net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 似乎处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用 BackgroundWorker 类方便地解决问题。该控件有三个事件:DoWork 、ProgressChanged 和 RunWorkerCompleted在程序中调用RunWorkerAsync方法则会启动DoWork事件的事件处理,当在事件处理过程中,调用 ReportProgr
8、ess方法则会启动ProgressChanged事件的事件处理,而当DoWork事件处理完成时,则会触发RunWorkerCompleted事件。您必须非常小心,确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged和 RunWorkerCompleted 事件与用户界面进行通信。示例代码: using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Lin
9、q;using System.Text;using System.Windows.Forms; namespace BKWorkerSample public partial class Form1 : Form public Form1() InitializeComponent(); / 声明一个BackgroundWorker 对象; BackgroundWorker worker = new BackgroundWorker(); / 在 Load 中设置 属性和 事件 private void Form1_Load(object sender, EventArgs e) /DoWor
10、k 事件; worker.DoWork += new DoWorkEventHandler(worker_DoWork); /设置 ProgressChanged 的事件 worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); /设置 RunWorkerCompleted 事件 worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); / 明确 要 报告 进展
11、, 如果不报告(一般是不给UI 线程通讯),可以设置为 false ,节省开销。 worker.WorkerReportsProgress = true; /明确 执行过程中 可以 取消 worker.WorkerSupportsCancellation = true; /在DoWork事件中调用你需要异步调用的程序 /如果需要传递任何数据给worker_RunWorkerCompleted,请使用e.Argument. void worker_DoWork(object sender, DoWorkEventArgs e) for (int i = 0; i C-B解释:(1)A在UI线程上
12、执行完后,开始Invoke,Invoke是同步(2)代码段B并不执行,而是立即在UI线程上执行InvokeMethod方法,即代码段C。(3)InvokeMethod方法执行完后,代码段C才在UI线程上继续执行。看看代码(二),Control的BeginInvokeprivate delegate void BeginInvokeDelegate();private void BeginInvokeMethod() /C代码段private void butBeginInvoke_Click(object sender, EventArgs e) /A代码段. this.BeginInvoke
13、(new BeginInvokeDelegate(BeginInvokeMethod); /B代码段.你觉得代码的执行顺序是什么呢?记好Control的Invoke和BeginInvoke都执行在主线程即UI线程上A-B-C慎重,这个只做参考。,我也不肯定执行顺序,如果有哪位达人知道的话请告知。解释:(1)A在UI线程上执行完后,开始BeginInvoke,BeginInvoke是异步(2)InvokeMethod方法,即代码段C不会执行,而是立即在UI线程上执行代码段B。(3)代码段B执行完后(就是说butBeginInvoke_Click方法执行完后),InvokeMethod方法,即代码
14、段C才在UI线程上继续执行。由此,我们知道:Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。那么,这个异步到底是什么意思呢?异步是指相对于调用BeginInvoke的线程异步,而不是相对于UI线程异步,你在UI线程上调用BeginInvoke ,当然不行了。摘自Invoke和BeginInvoke的真正涵义一文中的评论。BeginInvoke的原
15、理是将调用的方法Marshal成消息,然后调用Win32 API中的RegisterWindowMessage()向UI窗口发送消息。摘自Invoke和BeginInvoke的真正涵义一文中的评论。(二)我们用Thread来调用BeginInvoke和Invoke 我们开一个线程,让线程执行一些耗费时间的操作,然后再用Control.Invoke和Control.BeginInvoke回到用户UI线程,执行界面更新。代码(三) Thread调用Control的Invokeprivate Thread invokeThread;private delegate void invokeDelega
16、te();private void StartMethod() /C代码段. Control.Invoke(new invokeDelegate(invokeMethod); /D代码段.private void invokeMethod() /E代码段private void butInvoke_Click(object sender, EventArgs e) /A代码段. invokeThread = new Thread(new ThreadStart(StartMethod); invokeThread.Start(); /B代码段.你觉得代码的执行顺序是什么呢?记好Control的
17、Invoke和BeginInvoke都执行在主线程即UI线程上A-(Start一开始B和StartMethod的C就同时执行)-(C执行完了,不管B有没有执行完,invokeThread把消息封送(invoke)给UI线程,然后自己等待)-UI线程处理完butInvoke_Click消息后,处理invokeThread封送过来的消息,执行invokeMethod方法,即代码段E,处理往后UI线程切换到invokeThread线程。这个Control.Invoke是相对于invokeThread线程同步的,阻止了其运行。解释:1。UI执行A2。UI开线程InvokeThread,B和C同时执行,
18、B执行在线程UI上,C执行在线程invokeThread上。3。invokeThread封送消息给UI,然后自己等待,UI处理完消息后,处理invokeThread封送的消息,即代码段E4。UI执行完E后,转到线程invokeThread上,invokeThread线程执行代码段D代码(四) Thread调用Control的BeginInvokeprivate Thread beginInvokeThread;private delegate void beginInvokeDelegate();private void StartMethod() /C代码段. Control.BeginIn
19、voke(new beginInvokeDelegate(beginInvokeMethod); /D代码段.private void beginInvokeMethod() /E代码段private void butBeginInvoke_Click(object sender, EventArgs e) /A代码段. beginInvokeThread = new Thread(new ThreadStart(StartMethod); beginInvokeThread .Start(); /B代码段.你觉得代码的执行顺序是什么呢?记好Control的Invoke和BeginInvoke
20、都执行在主线程即UI线程上A在UI线程上执行-beginInvokeThread线程开始执行,UI继续执行代码段B,并发地invokeThread执行代码段C-不管UI有没有执行完代码段B,这时beginInvokeThread线程把消息封送给UI,单自己并不等待,继续向下执行-UI处理完butBeginInvoke_Click消息后,处理beginInvokeThread线程封送过来的消息。解释:1。UI执行A2。UI开线程beginInvokeThread,B和C同时执行,B执行在线程UI上,C执行在线程beginInvokeThread上。3。beginInvokeThread封送消息给
21、UI,然后自己继续执行代码D,UI处理完消息后,处理invokeThread封送的消息,即代码段E有点疑问:如果UI先执行完毕,是不是有可能过了段时间beginInvokeThread才把消息封送给UI,然后UI才继续执行封送的消息E。如图浅绿的部分。Control的BeginInvoke是相对于调用它的线程,即beginInvokeThread相对是异步的。因此,我们可以想到。如果要异步取耗费长时间的数据,比如从数据库中读大量数据,我们应该这么做。(1)如果你想阻止调用线程,那么调用代码(三),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方
22、法。(2)如果你不想阻止调用线程,那么调用代码(四),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。分类: C#/.NET 使用Invoke解决多线程间的控件访问出错在一个WinForm界面上有一个按钮(button1)和一个文本框(textBox1),在button1的点击事件处理程序中创建一个新的线程,并期望在新线程中更改textBox1的值,容易出错的代码如下:/ 按钮点击事件处理程序private void button1_Click(object sender, EventArgs e) /创建新线程 Thread proces
23、sorThread = null; processorThread = new Thread(new ThreadStart(Done); processorThread.IsBackground = true; processorThread.SetApartmentState(ApartmentState.STA); processorThread.Start();/ 更新textBox1值private void Done() textBox1.Text = ; 运行程序点击按钮后出错,提示:线程间操作无效: 从不是创建控件“textBox1”的线程访问它。下边我们用Invoke解决这一
24、问题:/ 按钮点击事件处理程序private void button1_Click(object sender, EventArgs e) /创建新线程 Thread processorThread = null; processorThread = new Thread(new ThreadStart(Done); processorThread.IsBackground = true; processorThread.SetApartmentState(ApartmentState.STA); processorThread.Start();/ 定义委托delegate void WriteInvoke(s
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1