InvokeRequired与Invoke

在多线程应用中将会涉及不同的线程访问同一控件的问题,C#中禁止跨线程直接访问控件。某个控件在被创建时就记下了是谁创建了它,即它的创建线程。如果从另一个线程调用该控件,那么必须使用控件的 Invoke 方法来将调用封送现在调用它的线程。(Invoke方法是控件的方法)

到底是哪个线程要使用该控件呢?需要用InvokeRequired来询问一下,如果当前调用线程不是控件的创建线程,则为 true;否则为 false。

例子:

代码段前提是lb是一个label控件,在主线程创建(不用了解具体在哪个线程,反正和RunTimer不在一个线程),RunTimer方法是在另一个线程创建,这时候我们需要RunTimer方法调用主线程上的label控件。

  private delegate void RunTimeDelegate();
        private void RunTime()
        {
            if (lb.InvokeRequired)
            {
                RunTimeDelegate d = RunTime;
                lb.Invoke(d);//Invoke方法是控件的,这里相当于把RunTime方法交于了委托d,
                //d中相当于把RunTimer方法从线程中提取出来放到委托中,此时控件的Invoke方法就可以
                 //    调用委托中的和RunTimer相同方法,这样控件使用RunTimer方法就在控件的线程中使用的。
            }
            else
            {
                time = endTime - TimeSpan.Parse(DateTime.Now.ToString("HH:mm:ss"));
                lb.Text = time.ToString();
            }

        }

 ,这里添加几个实例,第一个是无参数传递,跨线程控制picturebox1控件

  private delegate void DrawOnePicDelegate();//跨线程调用pictureBox1
        public void DrawOnePicture()
        {
            if (pictureBox1.InvokeRequired)//如果控件被非本线程调用,则返回true
            {
                DrawOnePicDelegate d = new DrawOnePicDelegate (DrawOnePicture);//将方法放入委托
                this.pictureBox1.Invoke(d);//从从控件创建线程中调用该方法
            }
            else
            {
                      //方法
                }                

  另一个是带参数传递控制listbox的内容,传入的参数就是list中应该显示的text

  delegate void AddItemCallback(string text);

        //这种方法演示如何在线程安全的模式下调用Windows窗体上的控件。
        private void AddItem(string text)
        {
            if (this.listBox1.InvokeRequired)
            {
                AddItemCallback d = new AddItemCallback(AddItem);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.listBox1.Items.Add(text);
            }
        }

  

 

添加一个自己在实践中遇到的问题:问题大致描述:首先开辟一个线程,线程中方法实现在label控件上实现秒计数。

这里添加最开始错误的代码:

            G_th = new System.Threading.Thread(ShowText);
            G_th.IsBackground = true;//设置线程为后台线程
            G_th.Start();//开始执行线程
        
//以上为建立一个线程,线程名字为G_th。
        
        private delegate void ShowTextDelegate();
        public void ShowText()
        {
            int P_int_count = 0;
            while (true)
            {
                P_int_count = ++P_int_count > 1000000 ? 0 : P_int_count;//计数器累加 
                if (lb_time.InvokeRequired)
                {
                    ShowTextDelegate d = new ShowTextDelegate(ShowText);
                    lb_time.Invoke(d);
                }
                else
                {                 
                    lb_time.Text = P_int_count.ToString();
                }
                //  Thread.Sleep(1000);//线程睡眠1秒
            }
        }
                   

  上述代码运行时会出现卡死,原因在于while的循环问题,至于为什么,我也没搞懂,哪位大神看到了也可以帮小弟解答下,后来我改了代码成为下面的样子,运行成功。我把while的循环放在了外面,label的委托仅仅只判断label的引用线程,所以,在平时工作中,应当注意,尽量不要在控件的委托中调用循环。

           G_th.IsBackground = true;//设置线程为后台线程
           G_th.Start();//开始执行线程
public void AddCount() { P_int_count = 0; while (true) { P_int_count = ++P_int_count > 1000000 ? 0 : P_int_count;//计数器累加 ShowText(); Thread.Sleep(1000);//线程睡眠1秒 } } private delegate void ShowTextDelegate(); public void ShowText() { if (lb_time.InvokeRequired) { ShowTextDelegate d = new ShowTextDelegate(ShowText); lb_time.Invoke(d); } else { lb_time.Text = P_int_count.ToString();//窗体中显示计数 } }

  

 

posted on 2017-12-04 19:28  Yulong5759  阅读(768)  评论(0编辑  收藏  举报