C#线程问题

时间:2009-11-30 08:46:47

标签: c# multithreading backgroundworker

为了与线程,代表和后台工作者一起玩,我正在整理一些小应用程序,我在其中一个方面遇到了一些麻烦。 我有一个Windows表单,带有文本框,按钮和richttext。 当我按下按钮时,文本框中的文本被用作实例化类的参数,如下所示:

public partial class Form1 : Form
{
    private BackgroundWorker backgroundWorker;

    public Form1()
    {
        InitializeComponent();            
    }

    private void button1_Click(object sender, EventArgs e)
    {   
        backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
        backgroundWorker.RunWorkerAsync();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        new Thread((ThreadStart)delegate()
        {
            this.BeginInvoke((ThreadStart)delegate()
            {
                foreach (string line in textBox1.Lines)
                {  
                    Dig digger = new Dig(line, textBox1.Text);
                    digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);

                    string response = digger.GetAllInfo();

                    richTextBox1.AppendText(response);
                    Application.DoEvents();
                }
            });
        }).Start();
    }

    void OnUpdateTicker(string msg)
    {
        new Thread((ThreadStart)delegate()
        {
            this.BeginInvoke((ThreadStart)delegate()
            {
                label4.Text = msg;
                Application.DoEvents();
            });
        }).Start();            
    }
}

调试时遇到'textBox1.Lines'抛出类型为'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'的异常 关于如何解决这个问题的任何提示?

3 个答案:

答案 0 :(得分:5)

首先,不需要在DoWork内创建新线程; BackgroundWorker的整个想法是DoWork在一个单独的线程上执行。其次,由于DoWork在单独的线程上执行,并且UI控件只能在UI线程上修改,因此需要正确调用这些更新。因此,worker_DoWork的重写版本可能如下所示:

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (string line in textBox1.Lines)
    {  
        Dig digger = new Dig(line, textBox1.Text);
        digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
        string response = digger.GetAllInfo();
        richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
    }
}

注意代码如何不显式生成任何新线程,以及AppendText方法调用如何通过Control.Invoke调用完成,强制它在UI线程上执行。

答案 1 :(得分:0)

主要原因是文本框不归后台线程所有。

您的UI线程拥有所有UI对象,并且当按下按钮时您正在启动后台线程。该后台线程不应该有权访问任何UI对象。

如果您想要使用文本框的值,则需要以另一种方式将其传递给后台线程。

Have a look here for an explanation (and solution).

答案 2 :(得分:0)

除非通过使用控件的.Invoke方法明确告诉程序可以执行,否则只能从主线程本身更新主线程的控件。

From: http://www.albahari.com/threading/part3.aspx

Control.Invoke

在多线程Windows窗体应用程序中,从创建控件的任何线程以外的任何线程调用控件上的方法或属性是非法的。必须使用Control.Invoke或Control.BeginInvoke方法将所有跨线程调用显式编组到创建控件的线程(通常是主线程)。人们不能依赖自动编组,因为它发生得太晚 - 只有当执行很好地进入非托管代码时,大量的内部.NET代码可能已经在“错误”的线程上运行 - 代码不是线程安全的。