写程序的时候经常会遇到跨线程访问控件的问题,看到不少人去设置Control.CheckForIllegalCrossThreadCalls = false;这句话是告诉编译器不要对跨线程访问作检查,可以实现访问,但是出不出错就不一定了。
个人建议使用委托delegate来实现。Windows中,UI是单线程的,即不允许多个线程直接访问它,通常它只由创建这个控件的线程(主线程)来控制,所以可以通过委托,告诉主线程对控件进行操作。
点击一个按钮,就创建一个线程添加一个item到ListView中,直接操作如下:
//添加item
private void AddItems() { this.listView1.Items.Add(new ListViewItem(new string[]{ Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString() })); }
按钮事件
private void button1_Click(object sender,EventArgs e) { Thread back_thread = new Thread(new ThreadStart(AddItems)); back_thread.Start(); }
运行,点击按钮,产生以下异常
修改方式
声明一个委托
private delegate void addItemHandler(string ThreadID);
声明一个RunBackThread方法,它负责告诉主线程要干什么
private void RunBackThread() { if (this.listView1.InvokeRequired) {
//声明一个AddItems的委托 addItemHandler add = new addItemHandler(AddItems);
//this.Invoke(委托,请求线程ID参数) this.Invoke(add,new object[]{Thread.CurrentThread.ManagedThreadId.ToString()}); } }
this.Invoke的意思是在主线程上执行指定的委托,还有this.BeginInvoke在主线程上异步执行指定的委托。
按钮事件
private void button1_Click(object sender,EventArgs e) { Thread back_thread = new Thread(new ThreadStart(RunBackThread)); back_thread.Start(); }
AddItems方法和原来一样
private void AddItems(string ThreadID) { this.listView1.Items.Add(new ListViewItem(new string[]{ ThreadID,//请求线程ID
Thread.CurrentThread.ManagedThreadId.ToString(),//主线程ID DateTime.Now.ToLongTimeString() })); }
这里之所以通过string ThreadID来获取请求线程id是因为AddItems这个方法是由主线程执行的,所以在AddItems方法里面通过Thread.CurrentThread.ManagedThreadId获取的id都是主线程id。
执行结果
这个图看起来应该很容易明白了吧。