C# 线程间操作无效

来源:互联网 发布:java中main函数 编辑:程序博客网 时间:2024/04/26 01:58

转载出处

许多新学习C#的新手进行界面操作的时候,在启用新线程时操作界面元素经常会遇到的问题,我曾经也是,特此来分享下经验:

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

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 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。


以上方法,不推荐使用第一种,当然做测试或者演示的时候可以,在真正的项目中,禁用提示着也失去了微软为了方面我们程序员的良苦用心,同时也可能造成不安全的问题,所以建议使用委托去实现!

0 0