【IT168技术文档】
今天在编写一个windows应用程序的时候碰到了一个小问题,程序需求是这样的,创建多个线程调用执行某个方法,Windows Form中有个Progress Bar控件用于显示已经执行完毕的进程数,即当所有的线程都运行完毕后,Progress Bar的进度也到头了。先给出初步的实现方式:
当调试执行这段程序时在this.progressBar1.Value = count * 10;处,报出了 InvalidOperationException Cross-thread operation not valid异常,在网上搜索一番后,发现产生该问题的原因如下。private const int MAXTHREAD = 100; //最大线程数 private int n = 0, count = 0; //实际线程数、已结束的线程数 private void StartTest() { n = int.Parse(txtThreadCount.Text); //线程数 progressBar1.Maximum = n * 10; //设置ProgressBar的最大值 Thread thread = null; List<Thread> threads = new List<Thread>(MAXTHREAD); ReadTableTest t = new ReadTableTest(tableName, fileds); t.ThreadExitEvent += new ThreadExit(OnThreadExit); //线程执行完毕后通知主界面 try { //创建线程 for (int i = 0; i < n; i++) { thread = new Thread(new ThreadStart(t.Run)); threads.Add(thread); } //开始调用接口 foreach (Thread tt in threads) { tt.Start(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } //线程执行完毕后回调 public void OnThreadExit() { count++; this.progressBar1.Value = count * 10; //设置ProgressBar的进度 //判断是否全部进程已结束 if (count == n) { MessageBox.Show("所有线程已执行完毕!"); ClearState(); } }
问题原因
由于 Windows 窗体控件本质上不是线程安全的。因此如果有两个或多个线程适度操作某一控件的状态(set value),则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用和死锁的情况。于是在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException
解决方案
对于该异常的解决方案有两种,一种是关闭该异常检测的方式来避免异常的出现,经过测试发现此种方法虽然避免了异常的抛出,但是并不能保证程序运行结果的正确性,对于此例来说,经常是全部线程结束时,进度条的显示还未到头呢。下面再来看看更加优雅的解决方案,即通过保证线程的安全性来避免该异常,先来看看两种方案的代码。
方案1
public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false; }