C#线程间操作无效: 从不是创建控件" XX" 的线程访问它

转自:http://www.arasplm.net/index.php/zh/community/myblog/c-xx-.html

前些天做的要使用到线程的项目,现在和大家分享一下感受!

以下面小列子为例,给出这个问题的解决办法。下面的列子是以一个计数器为列讲解的。

public Form1()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
// 创建线程
Thread newThread = new Thread(new ThreadStart(Count)); newThread.Start();

}
public void Count()
{
for (int i = 0; i < 100; i++)
{
lblCount.Text = i.ToString();//此时就会报出“线程间操作无效: 从不是创建控件" lblCount" 的线程访问它”;
Thread.Sleep(1000);
}
}

 

解决办法一:设置 Control.CheckForIllegalCrossThreadCalls = false;

public Form1()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
// 方法一 获取或设置一个值,该值指示是否捕获对错误线程的调用,这些调用在调试应用程序时访问控件的 Handle 属性 // Control.CheckForIllegalCrossThreadCalls = false;
// 创建线程
Thread newThread = new Thread(new ThreadStart(Count)); newThread.Start();

}
public void Count()
{
for (int i = 0; i < 100; i++)
{
lblCount.Text = i.ToString();
Thread.Sleep(1000);
}
}

解决办法二:使用Invoke方法

public Form1()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{

//Invoke方法是同步的方法,所以执行过程是有先后顺序的,所以就不会出现那个异常了
//创建线程
Thread newThread = new Thread(new ThreadStart(Count));
//加上这句话,否则在关闭窗体时会出现如下错误:在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。
newThread.IsBackground = true;
newThread.Start();

}
public void Count()
{
for (int i = 0; i < 100; i++)
{
this.Invoke((EventHandler)(delegate
{
lblCount.Text = i.ToString(); }));
//这个不能放在Invoke里面,不然又Form1窗体假死情况
Thread.Sleep(1000);
}
}
解决方法三:通过BeginInvoke方法和委托来实现

public Form1()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
mydelegate = new myDelegate(ShowMessage); Thread newThread = new Thread(Count);
//加上这句话,否则在关闭窗体时会出现如下错误:在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。
newThread.IsBackground = true;
newThread.Start();

}
public void Count()
{
for (int i = 0; i < 100; i++)
{ Thread.Sleep(1000);
this.BeginInvoke(mydelegate, new object[] { i });
}
}
public void ShowMessage(int i)
{
lblCount.Text = i.ToString();
}

以上总结:

因为第一种方法只是简单的将错误提示禁用了,仍然存在跨线程调用控件的问题。为此可能造成两个线程同时或者循环改变该控件的状态导致线程死锁。 Invoke方法是同步的方法,所以执行过程是有先后顺序的,所以就不会出现那个异常了。而第三种方法只是第二种方法的另一种形式而已,在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。

正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

你可能感兴趣的:(C#,Winform)