Threading基础知识

问题描述:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 


namespace testThreads 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 

     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 

     } 

     public void countToLots() 
     { 
      for (int i = 0; i < 10000000; i++) 
      { 
       textBox1.Text = "Counting to 10000000, value is " + i + Environment.NewLine; 
      } 
     } 

     public void countToZero() 
     { 
      for (int i = 10000000; i > 0; i--) 
      { 
       textBox2.Text = "Counting to 0, value is " + i + Environment.NewLine; 
      } 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      Thread countUp = new Thread(new ThreadStart(countToLots)); 
      Thread countDown = new Thread(new ThreadStart(countToZero)); 
      countUp.Start(); 
      countDown.Start(); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      textBox3.Text = "Bobby bob bob " + Environment.NewLine; 
     } 
    } 
} 

我真的需要尝试并得到这个 - 我只是不明白为什么我得到一个错误消息的理论背后。有人可以帮我吗?Threading基础知识

跨线程操作无效: 控制“textBox1的”从比它的线程上创建 以外的 线程访问。

UI控件具有“线程关联”;他们不要想要被除UI线程之外的任何东西所触及;包括读写属性。应该通过使用InvokeBackgroundWorker从UI线程完成对.Text的分配。

例如:

public void countToLots() 
{ 
    for (int i = 0; i < 10000000; i++) 
    { 
     // running on bg thread 
     textBox1.Invoke((MethodInvoker) delegate { 
      // running on UI thread 
      textBox1.Text = "Counting to 10000000, value is " 
         + i + Environment.NewLine; 
     }); 
     // running on bg thread again 
    } 
} 

,这种类型的线程切换有开销。您不应该每次迭代都回调 - 例如,您应该(例如)每隔[n]次迭代更新一次UI - 以上为例,每10000次更新一次。

+0

我仍然以相同的方式调用该方法吗? – tom 2011-02-09 10:44:17

您不能使用与创建(称为新建)控件的线程不同的线程使用Form控件的方法或属性。

为了做到这一点只是做:

public void countToLots() 
    { 
     for (int i = 0; i < 10000000; i++) 
     { 
      SetText("Counting to 10000000, value is " + i + Environment.NewLine); 
     } 
    } 

    public void SetText(string text) 
    { 

     if (this.textBox1.InvokeRequired()) 
     { 
      Action<string> auxDelegate = SetText; 
      this.BeginInvoke(auxDelegate,text); 
     } 
     else 
     { 
      this.textBox1.Text = text; 
     } 
    } 

什么方法与BeginInvoke的做只是再次调用从创建控制线程使用setText方法可以。

好吧,关于为什么控件具有UI线程亲和力的理论。

如果您编程时间足够长,您会记得表格和快速应用程序开发不是标准的日子。在那些日子里,仅仅把控制权转移到一种形式中是非常罕见的......一切都是由旧学校完成的。

现在,在windows中,“老派”做事的方式涉及到定义WindowProc

WindowProc是一个被调用来处理应用程序消息的函数(注意我说的是,不是)。该函数在主程序线程上运行,负责处理应用程序收到的每条消息,包括用户界面的绘制和刷新。

现在所有这些大部分都是自动化的,所以当你创建一个表单时,负责完成所有工作的代码是自动生成的,你不必担心这个......但它仍然存在。当然,如果负责绘制用户界面及其所有控件的线程是主线程,那么您将看到如何从其他线程更改事物可能会因竞争条件等而扰乱应用程序本身。另外,由于UI处理是自动生成的,因此您不能只将两个标准线程使用的同步机制放在一起,因为您只能访问一个线程上的代码,而不能访问主窗口回调。

从某种意义上说,BeginInvoke将为您做的事情是将消息传递给主线程,告诉它在时间合适时亲切地处理委托,从而将执行委托给主线程。