zoukankan      html  css  js  c++  java
  • WinForm 子线程修改主线程(UI线程)

    原文http://www.cnblogs.com/SkySoot/archive/2012/03/14/2396552.html

    我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段:

     1 public partial class TestThread : Form
     2 {
     3     public TestThread()
     4     {
     5         InitializeComponent();
     6     }
     7  
     8     Thread thread;
     9  
    10     void SetText(string str)
    11     {
    12         this.textBox1.Text = str;
    13     }
    14  
    15     void DoSomething()
    16     {
    17         // do......
    18         SetText("XXX");
    19     }
    20  
    21     private void buttonX1_Click(object sender, EventArgs e)
    22     {
    23         thread = new Thread(new ThreadStart(DoSomething));
    24         thread.IsBackground = true;
    25         thread.Start();
    26     }
    27 }

    image

    在VS2005或者更高版本中,只要不是在控件的创建线程(一般就是指UI主线程)上访问控件的属性就会抛出这个错误,解决方法就是利用控件提供的Invoke和BeginInvoke把调用封送回UI线程,也就是让控件属性修改在UI线程上执行.

    下面给出修改后正确调用的代码:

    比较直接的修改方法像这样:

     1 public partial class TestThread : Form
     2 {
     3     delegate void ChangeTextBoxValue(string str); // 新增委托代理
     4  
     5     public TestThread()
     6     {
     7         InitializeComponent();
     8     }
     9  
    10     Thread thread;
    11  
    12     void SetText(string str)
    13     {
    14         this.textBox1.Text = str;
    15     }
    16  
    17     void DoSomething()
    18     {
    19         // do......
    20         this.BeginInvoke(new ChangeTextBoxValue(SetText),"XXX"); // 也可用 this.Invoke调用
    21     }
    22  
    23     private void buttonX1_Click(object sender, EventArgs e)
    24     {
    25         thread = new Thread(new ThreadStart(DoSomething));
    26         thread.IsBackground = true;
    27         thread.Start();
    28     }
    29 }

    不过,要考虑到也许程序中不止子线程会调用 DoSomething()方法,也许主线程或其他模块多处调用这个方法,最漂亮的改法是这样:

     1 public partial class TestThread : Form
     2 {
     3     delegate void ChangeTextBoxValue(string str);
     4  
     5     public TestThread()
     6     {
     7         InitializeComponent();
     8     }
     9  
    10     Thread thread;
    11  
    12     void SetText(string str)
    13     {
    14         if (this.InvokeRequired) // 获取一个值指示此次调用是否来自非UI线程
    15         {
    16             this.Invoke(new ChangeTextBoxValue(SetText), str);
    17         }
    18         else
    19         {
    20             this.textBox1.Text = str;
    21         }            
    22     }
    23  
    24     void DoSomething()
    25     {
    26         // do......
    27         SetText("ABCDEFG");
    28     }
    29  
    30     private void buttonX1_Click(object sender, EventArgs e)
    31     {
    32         thread = new Thread(new ThreadStart(DoSomething));
    33         thread.IsBackground = true;
    34         thread.Start();
    35     }
    36 }

    this.Invoke(new ChangeTextBoxValue(SetText), str) // 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托

    this.BeginInvoke(new ChangeTextBoxValue(SetText), str); // 在创建控件的基础句柄所在线程上,用指定的参数异步执行指定委托。

    无论是同步还是异步,单步跟踪调试代码会发现,这些方法还是会回到UI线程执行,其中通过了代理而已.

    这两个方法向UI线程的消息队列中放入一个消息,当UI线程处理这个消息时,就会在自己的上下文中执行传入的方法,换句话说凡是使用BeginInvoke和Invoke调用的线程都是在UI主线程中执行的,所以如果这些方法里涉及一些静态变量,不用考虑加锁的问题.

  • 相关阅读:
    shell遍历文件夹并执行命令
    安装PIL的坑
    iptables不小心把127.0.0.1封了,导致redis连不上
    python读取中文
    不要在基类析构函数中调用纯虚函数,否则运行时会报错“pure virtual method called”
    阿里云64位centos6.3系统上编译安装redis
    Git
    Easy Mock
    Sortable
    几个框架
  • 原文地址:https://www.cnblogs.com/MDK-L/p/3791313.html
Copyright © 2011-2022 走看看