日期:2014-05-18  浏览次数:21713 次

在winform中多线程中修改界面相关的控件时运行正常,但调试是出错的问题
在程序中有时要执行耗时较长的任务时,一般是另外开一个线程,在其中执行耗时较长的任务,此时ui还可以 继续响应用户操作.现在想在ui中显示此任务完成的进度,给用户一定的提示,便在线程中直接修改控件.


代码如下:
 public partial class Form1 : Form
  {
  private Thread _task;

  public Form1()
  {
  InitializeComponent();
  }

  private void Form1_Load(object sender, EventArgs e)
  {
  _task = new Thread(new ThreadStart(DoTask));
  _task.Start();
  }

  private void DoTask()
  {
  //模拟毫时任务
  try
  {

  for (int i = 0; i < 100; i++)
  {
  for (int j = 0; j < 100000000; j++)
  {
  j++;
  j--;
  }
  //在截面中显示此任务的进度
  Lb_Result.Text = i.ToString();
  }
  }
  catch (Exception Exc)
  {
  System.Diagnostics.Trace.WriteLine(Exc.Message);
  }
  }

  private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  {
  _task.Abort();
  }
  }
以上代码直接运行时没有问题(Alt+ F5),但当要调试程序时(F5),便报错,提示Cross-thread operation not valid: Control 'Lb_Result' accessed from a thread other than the thread it was created on. 
请问是什么原因,如何避免.

看过相关的贴子,可以用静态变量,确实可以实现主线程和支线程的互操作,但是对于显示内容的控件不能用此方法,因为控件必须在主线程中创建.我现在是在主线中用个timer定是从静态变量里取信息修改控件内容,确实可以实现调试时也不报错,但是感觉实现起来太不优雅了.应该还有其他好的办法的.

还有就是想实现我所提级的在界面中显示 任务线程的进度情况.有没有其他的好的方法.请给出相关示例,谢谢.

------解决方案--------------------
微软建议,你这种情况适用backgroundworker
------解决方案--------------------
通过回调
Invoke 或者 BeginInvoke
------解决方案--------------------
http://topic.csdn.net/u/20071213/03/6d898922-0d80-4a3b-9bd9-6a1aca56adc2.html
------解决方案--------------------
异步委托, 更新时加 lock
------解决方案--------------------
用2L的invoke方法。

记住在非主线程中画图是很忌讳的。
因为多个线程同时画图可能产生对资源申请的死锁或者使主线程处于假死状态。
这涉及到windows内部对象的申请、释放、临界区等概念。

我在使用mfc开发的时候就遇到过和lz一样的问题。

解决方法:
使用invoke,当需要画图的时候请求主线程来画图。
这样就不会出现死锁和假死。

windows编程的一句箴言:不要在非界面线程(即非主线程)中直接画图。

------解决方案--------------------
同backgroundworker LOCK之类没关系。WINDOWS所有的GDI对象都不是线程安全的,记住了。所以不管用什么语言C还是以C++,C#(JAVA的伪窗口不清楚)。只要创建的是WINDOWS窗体,就不能跨线程访问。
 使用

if(InvokeRequired )
{
BeginInvoke(...本函数);
}
else
{
更新窗体代码。
}