关于C# this.InvokeRequired的问题
请问各位兄弟,我这样子写的一段程序,是用来线程去调用窗体控件,进行复制,可是我这样写,程序会卡死的~~~这是为什么了??还有就是this.Invoke与this.BeginInvoke的怎样区别的?如果用了怎样this.BeginInvoke之后,需要调用this.EndInvoke吗?
public delegate void DeleTtt(); public delegate void DeleTttt(); public partial class Form2 : Form { public Form2() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Thread t1 = new Thread(new ThreadStart(progressBarSample)); t1.IsBackground = true; t1.Start(); Thread t2 = new Thread(new ThreadStart(lblText)); t2.IsBackground = true; t2.Start(); } private void progressBarSample() { if (this.InvokeRequired) { this.Invoke(new DeleTttt(lblText)); } else { for (int i = 0; i < this.progressBarSample2.Properties.Maximum; i += 50) { this.progressBarSample2.Position += i; Thread.Sleep(1500); } } } private void lblText() { if (this.InvokeRequired) { this.Invoke(new DeleTtt(lblText)); } else { while (true) { label2.Text += "."; if (label2.Text.Length >= 4) { label2.Text = String.Empty; } Thread.Sleep(1000); } } } }
Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。
那么,这个异步到底是什么意思呢?
异步是指相对于调用BeginInvoke的线程异步,而不是相对于UI线程异步,你在UI线程上调用BeginInvoke ,当然不行了。----摘自"Invoke和BeginInvoke的真正涵义"一文中的评论。
BeginInvoke的原理是将调用的方法Marshal成消息,然后调用Win32 API中的RegisterWindowMessage()向UI窗口发送消息。----摘自"Invoke和BeginInvoke的真正涵义"一文中的评论。
(二)我们用Thread来调用BeginInvoke和Invoke
我们开一个线程,让线程执行一些耗费时间的操作,然后再用Control.Invoke和Control.BeginInvoke回到用户UI线程,执行界面更新。
代码(三) Thread调用Control的Invoke
private Thread invokeThread;
private delegate void invokeDelegate();
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的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执行A
2。UI开线程InvokeThread,B和C同时执行,B执行在线程UI上,C执行在线程invokeThread上。
3。invokeThread封送消息给UI,然后自己等待,UI处理完消息后,处理invokeThread封送的消息,即代码段E
4。UI执行完E后,转到线程invokeThread上,invokeThread线程执行代码段D
代码(四) Thread调用Control的BeginInvoke
private Thread beginInvokeThread;
private delegate void beginInvokeDelegate();
private void StartMethod(){
//C代码段......
Control.BeginInvoke(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都执行在主线程即UI线程上
A在UI线程上执行----->beginInvokeThread线程开始执行,UI继续执行代码段B,并发地invokeThread执行代码段C-------------->不管UI有没有执行完代码段B,这时beginInvokeThread线程把消息封送给UI,单自己并不等待,继续向下执行-------->UI处理完butBeginInvoke_Click消息后,处理beginInvokeThread线程封送过来的消息。
解释:
1。UI执行A
2。UI开线程beginInvokeThread,B和C同时执行,B执行在线程UI上,C执行在线程beginInvokeThread上。
3。beginInvokeThread封送消息给UI,然后自己继续执行代码D,UI处理完消息后,处理invokeThread封送的消息,即代码段E
有点疑问:如果UI先执行完毕,是不是有可能过了段时间beginInvokeThread才把消息封送给UI,然后UI才继续执行封送的消息E。如图浅绿的部分。
Control的BeginInvoke是相对于调用它的线程,即beginInvokeThread相对是异步的。
因此,我们可以想到。如果要异步取耗费长时间的数据,比如从数据库中读大量数据,我们应该这么做。
(1)如果你想阻止调用线程,那么调用代码(三),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。
(2)如果你不想阻止调用线程,那么调用代码(四),代码段D删掉,C改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段E改为更新界面的方法。
[解决办法]
if (this.InvokeRequired) { this.Invoke(new DeleTttt(lblText)); } else { }
[解决办法]
我觉得Thread.Sleep()调用的地方错误,应该在每一次修改主线程UI的内容时都要检查if (this.InvokeRequired):
我修改了一下,你看看可不可以。
private void button1_Click(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(progressBarSample));
t1.IsBackground = true;
t1.Start();
Thread t2 = new Thread(new ThreadStart(lblText));
t2.IsBackground = true;
t2.Start();
}
private void progressBarSample()
{
if (progressBarSample2.Properties.Maximum > progressBarSample2.Position)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(ProcessProgressBar));
}
else
{
ProcessProgressBar();
}
Thread.Sleep(1500);
}
}
private void ProcessProgressBar()
{
var count = progressBarSample2.Properties.Maximum - progressBarSample2.Position;
if (count > 50)
{
progressBarSample2.Position += 50;
}
else
{
progressBarSample2.Position += count;
}
}
private void lblText()
{
while (true)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(ProcessLblText));
}
else
{
ProcessLblText();
}
Thread.Sleep(1000);
}
}
private void ProcessLblText()
{
label2.Text += ".";
if (label2.Text.Length >= 4)
{
label2.Text = String.Empty;
}
}