• 线程间操作无效: 从不是创建控件“button1”的线程访问它。


    net2后是不能跨线程访问控件的。,窗体上的控件是当前线程创建的,当用户异步执行一个方法:在该方法中给窗体上的控件赋值,记住:当执行一个异步委托的时候,其实

    就是开了一个线程去执行那个方法,这样就会报错:线程间操作无效: 从不是创建控件“某某某”的线程访问它。

    C# WinForm开 发中,这是一个比较常见的异常:线程间操作无效,从不是创建控件“xxx”的线程访问它。这个异常来源于.NET2的一个限制:工作线程不能访问窗口线程 创建的控件。解决方法主要有两种,一种是在窗口线程中设置CheckForIllegalCrossThreadCalls = false ;另一种方式比较麻烦,使用委托的方式调用Invoke方法。

     public Form1()
            {
                InitializeComponent();
                Control.CheckForIllegalCrossThreadCalls = false;
            }

    但以上不是推荐的方法。更好的办法是用委托解决

    复制代码
      private void button1_Click(object sender, EventArgs e)
            {
                new Action(show).BeginInvoke(null, null);
            }
    
            void show()
            {
                //异步外的方法。这样窗体不会假死
                while (true)
                {
                    Thread.Sleep(2000);
                    Action ac = new Action(showText);
                    this.Invoke(ac); //在同步方法里面实现更新窗体上的数据
                }
            }
    
            /// <summary>
            /// 更新数据
            /// </summary>
            void showText()
            {
                richTextBox1.AppendText("更新
    ");
            }
    复制代码

    或者使用InvokeRequired属性判断

     /*
                 // 摘要:
            //     获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
            //
            // 返回结果:
            //     如果控件的 System.Windows.Forms.Control.Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke
            //     方法对控件进行调用),则为 true;否则为 false。

    复制代码
    private void button1_Click(object sender, EventArgs e)
            {
                //new Action(show).BeginInvoke(null, null);
                new Action(show1).BeginInvoke(null, null);
            }
            void show1()
            {
                while (true)
                {
                    Thread.Sleep(2000);//模拟等待效果
                    show2();
                }
            }
    
            void show2()
            {
                //说明的当前外部线程
                /*
                 // 摘要:
            //     获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
            //
            // 返回结果:
            //     如果控件的 System.Windows.Forms.Control.Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke
            //     方法对控件进行调用),则为 true;否则为 false。
                 */
                if (InvokeRequired)
                {
                    /*既然是外部线程,那么就没有权限访问主线程上的控件
                     * 故要主线程访问,开启一个异步委托捆绑要执行的方法
                     * 交给主线程执行
                     */
                    Action ac = new Action(show2);
                    this.Invoke(ac); //这里执行后。则InvokeRequired就为false。因为此时已经是主线程访问当前创建的控件
                }
                else
                {
                    richTextBox1.AppendText("更新77
    ");
                }
            }
    复制代码

    看了第一段代码是不是很不爽的感觉。showText()方法就一条赋值语句,则也独立成一个方法。这里可以简化代码,用匿名函数或者用更简单的lambda表达式(这个方法不需要其他用户调用,就可以考虑用匿名函数)。那么看简化后的代码

    复制代码
      private void button1_Click(object sender, EventArgs e)
            {
                new Action(show).BeginInvoke(null, null);
            }
    
      void show()
            {
                //异步外的方法。这样窗体不会假死
                while (true)
                {
                    Thread.Sleep(2000);//在异步方法外实现等待,这样窗体不会假死
                    //Action ac = new Action(showText);
                    //this.Invoke(ac); //在同步方法里面实现更新窗体上的数据
    
                    //匿名函数
                    Action at = new Action(delegate(){ richTextBox1.AppendText("更新
    "); });
                    //lambda表达式更简单
                    Action at1 = new Action(()=> { richTextBox1.AppendText("更新
    "); });
                    this.Invoke(at);

            //这里this。代表当前窗体(控件)上的线程执行此方法
                    //这里不一定是this,只要是当前窗体上的控件都可以,比如
                    // button1.Invoke(at);
                    this.Invoke(at);//Invoke:在拥有此控件的基础窗口句柄的线程上执行指定的委托。
                    //richTextBox1.Invoke(at);
                    //button1.Invoke(at);
    } }
    复制代码

     我们用同样的方法把InvokeRequired属性判断的那段代码也用lambda表达式简写

    复制代码
     private void button1_Click(object sender, EventArgs e)
            {
                new Action(show1).BeginInvoke(null, null);
            }
      void show1()
            {
                //while (true)
                //{
                //    Thread.Sleep(2000);//模拟等待效果
                //    show2();
                //}
    
                for (int i = 0; i < 5000; i++)
                {
                    Thread.Sleep(2000);//模拟等待效果
                    //show2();
    
                    if (InvokeRequired)
                    {
                        Action ac = new Action(() => { richTextBox1.AppendText("更新767
    "); });
                        this.Invoke(ac); //这里执行后。则InvokeRequired就为false。因为此时已经是主线程访问当前创建的控件
                    }
                }
    
            }
    复制代码

     以上两种方法都是实现相同的效果,差别就是多了一个InvokeRequired判断是否需要执行Invoke()方法

    参考:http://www.cnblogs.com/txw1958/archive/2012/08/21/2649192.html

  • 相关阅读:
    C# 中的高性能计时器(Daniel Strigl著,野比译)(转)
    C#中SerialPort类 随笔
    迭代器
    枚举数与可枚举类型(笔记)
    jQuery的关注与学习
    敏捷开发的4句宣言 大数据
    bat脚本
    c++动态库中回调函数使用
    C++中遍历lua table
    vs2010编译release版本却依赖debug版本库msvcrd100.dll
  • 原文地址:https://www.cnblogs.com/asdyzh/p/9858209.html
Copyright © 2020-2023  润新知