Control的Invoke和BeginInvoke

Control 的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么 的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。
为了这个我还专门写了代码实验了一把,果然如此,不管是Invoke还是BeginInvoke都会造成界面假死,说明线程被阻塞了。
就是在我们跨线程修改UI界面的时候,在非控件创建线程中操作控件这个是不安全的,代码在执行的时候是会报错的。

我们只能把要调用的方法封装为委托,然后通过BeginInvoke这个方法来执行。(调用的方法)

        private void button1_Click(object sender, EventArgs e)
        {
            var task = Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < 9999; i++)
                {
                    AddItemIntoComBox(i);
                    Thread.Sleep(1000);
                }
            });
        }
        void AddItemIntoComBox(int index)
        {
            if (comboBox1.InvokeRequired)
                comboBox1.BeginInvoke(new Action<int>(AddItemIntoComBox), new object[] { index });
            else
                comboBox1.Items.Add("测试" + index);
        }

代码还是很容易理解的。启动一个新的线程,线程里面循环999次,每次干的活都一样,就是调用AddItemIntoComBox这个方法,并把循环次数给了这个方法

执行完这个方法后睡一秒。

AddItemIntoComBox 这个方法就更简单了。判断是否在UI线程中,如果不是进行一个委托调用。 给combox里面添加一个值。

让我在看另一段代码:

        private void button1_Click(object sender, EventArgs e)
        {
            var task = Task.Factory.StartNew(AddItemIntoComBox);
        }

        void AddItemIntoComBox()
        {
            if (comboBox1.InvokeRequired)
            {
                comboBox1.BeginInvoke(new Action(AddItemIntoComBox));
            }
            else
            {
                for (int i = 0; i < 999; i++)
                {
                    comboBox1.Items.Add("测试" + i);
                    Thread.Sleep(1000);
                }
            }
        }

差别不是很大,只是循环的位置变了。

但影响却很大,第二种写法会造成主线程阻塞。原因很简单:

跨线程更新UI界面不是真正的跨线程,而是将方法作为一个委托(指针函数)封装到了主线程。说的简单点其实还是在主线程中操作。

 

posted on 2016-09-05 16:50  larry_叶良辰  阅读(320)  评论(0)    收藏  举报

导航