【问题标题】:Cross threading problem: Invalid operationexception was handeled跨线程问题:处理了无效的操作异常
【发布时间】:2011-07-30 21:57:59
【问题描述】:

我正在制作一个程序,从 txt 文件导入电子邮件并向他们发送消息,但我遇到了一个问题,目前我正在使用一个线程来发送邮件方法以防止程序停止响应,确切的问题标题是:

处理了无效的操作异常
>>> 跨线程操作无效:
    控件“richTextBox1”从创建它的线程以外的线程访问。

这里是代码

    int success = 0;
    int failed = 0;
    int total = 0;
    bool IsRunning;

List<string> list = new List<string>();

private void addmails()
    {
        string path = textBox2.Text;

        foreach (string line in File.ReadAllLines(path))
        {
            list.Add(line);
        }
        IsRunning = true;
    }
    private void sendmails(object sender, DoWorkEventArgs e)
    {
        if (IsRunning == true)
        {
            if (checkBox1.Checked != true)
            {
                SmtpClient client = new SmtpClient(comboBox1.Text);
                client.Credentials = new NetworkCredential(textBox6.Text, textBox7.Text);
                MailMessage message = new MailMessage();
                message.From = new MailAddress(textBox3.Text, textBox1.Text);
                message.Subject = textBox4.Text;
                //message.Body = richTextBox1.Text;
                if (textBox5.Text != "")
                {
                    message.Attachments.Add(new Attachment(textBox5.Text));
                }

                foreach (string eachmail in list)
                {
                    if (IsRunning == true)
                    {
                        try
                        {
                            message.To.Add(eachmail);
                            client.Send(message);
                            listBox1.Items.Add("Successfully sent the message to  : " + eachmail);
                            success++;
                        }
                        catch
                        {
                            listBox1.Items.Add("Failed to send the message to  : " + eachmail);
                            failed++;
                        }
                        message.To.Clear();

                        total++;

                        Thread.Sleep(15);

                        label18.Text = total.ToString();
                        label19.Text = success.ToString();
                        label21.Text = failed.ToString();

                    }
                    else
                    {
                        break;
                    }
                }

                IsRunning = false;
                button3.Text = "Send";
            }

        }
    }

    private void button3_Click(object sender, EventArgs e)
    {
        if (button3.Text == "Send")
        {
            tabControl1.SelectedTab = tabPage3;
            button3.Text = "Stop";

            addmails();

           // IsRunning = true;

            Thread t2 = new Thread(sendmails); // when using that thread i get a cross threading error
            t2.Start();

        }
        else
        {
            IsRunning = false;
            button3.Text = "Send";
            MessageBox.Show("Sending Mails Operation has been terminated","Abort",MessageBoxButtons.OK,MessageBoxIcon.Information);
        }

【问题讨论】:

  • 请尽量避免问题描述仅出现在标题中。您应该发布实际的异常以及它发生的位置。
  • 正如@Lasse V. Karlsen 所说,您看到的实际异常是什么?
  • 确切的问题标题是:处理了无效的操作异常>>> 跨线程操作无效:控件'richTextBox1'从创建它的线程以外的线程访问。

标签: c# winforms multithreading


【解决方案1】:

问题在于 UI (WinForms) 控件正在从非 UI 线程(新生成的线程)访问。

不要那样做。可以使用Control.Invoke/BeginInvokeSynchronizationContext.Post/Send,具体取决于具体需要什么。使用上面的关键字查找此问题将导致许多示例。

另一种选择是使用BackgroundWorker 对象和RunWorkCompleted/ProgressChanged 事件并在发送任何内容之前传入所有必需的信息。 (例如,不要在新线程中“读取”用户界面。)

编码愉快。


资源/补充阅读:

How do I change a ComboBox data from another thread? - Invoke/BeginInvoke 的小例子,包括一个不错的小包装器。解决帖子中的问题所需的一切。

What's up with BeginInvoke? - 讨论 WinForms 控件访问的限制、Windows 消息传递的工作方式以及 Invoke/BeginInvoke 的使用。很好读。

Understand SynchronizationContext - 与上一个类似,但侧重于使用 SC 而不是 Invoke/BeginInvoke。仅在上一个链接之后阅读。

BackgroundWorker and SynchronizationContext - 我更喜欢使用 SynchronicationContext 而不是 Control.Invoke/BeginInvoke(我更喜欢界面,无需检查 InvokedRequired 或类似的)。这在 BGW 的上下文中讨论了 SC。从 2005 年开始,它使用旧的“委托”语法。如果使用 SC,请确保使用从 UI 线程获得的 SC。请参阅 Why is SynchronizationContext.Current null? 在我的应用程序中,我经常使用静态的 Send/Post 包装方法来处理所有细节。

【讨论】:

  • 如果您提供一些解释代码,我将不胜感激。并非常感谢您清理一切
  • @BOSS 我已经用一些(相对不错的)文章更新了我的帖子。
【解决方案2】:

您正在使用从不同线程而不是创建它们的线程访问 UI 成员,当您想从另一个线程访问 control 成员或方法时,您必须使用 Control.Invoke。 所以,要让它发挥作用,你必须“我真的不会这样做,只是回答你的问题”:

if (IsRunning == true)
{
    bool checkbox1Checked;
    string textBox6Text;
    string textBox7Text;
    string textBox3Text;
    string textBox1Text;
    string textBox4Text;
    string richTextBox1Text;
    string textBox5Text;

    MethodInvoker getValues = new MethodInvoker(delegate()
    {
        checkbox1Checked = checkbox1.Checked;
        textBox6Text = textBox6.Text;
        textBox7Text = textBox7.Text;
        textBox3Text = textBox3.Text;
        textBox1Text = textBox1.Text;
        textBox4Text = textBox4.Text;
        richTextBox1Text = richTextBox1.Text;
        textBox5Text = textBox5.Text;
    });

    if (this.InvokeRequired)
    {
        this.Invoke(getValues);
    }
    else
    {
        getValues();
    }

    if (checkBox1Checked != true)
    {
        SmtpClient client = new SmtpClient(comboBox1Text);
        client.Credentials = new NetworkCredential(textBox6Text, textBox7Text);
        MailMessage message = new MailMessage();
        message.From = new MailAddress(textBox3Text, textBox1Text);
        message.Subject = textBox4Text;
        //message.Body = richTextBox1Text;
        if (textBox5Text != "")
        {
            message.Attachments.Add(new Attachment(textBox5Text));
        }

        foreach (string eachmail in list)
        {
            if (IsRunning == true)
            {
                try
                {
                    message.To.Add(eachmail);
                    client.Send(message);

                    MethodInvoker addToListBox = new MethodInvoker(delegate()
                    {
                        listBox1.Items.Add("Successfully sent the message to  : " + eachmail);
                    }); 
                    if (listBox1.InvokeRequired)
                    {
                        listBox1.Invoke(addToListBox);
                    }
                    else
                    {
                        addToListBox();
                    }

                    success++;
                }
                catch
                {
                    MethodInvoker addToListBox = new MethodInvoker(delegate()
                    {
                        listBox1.Items.Add("Failed to send the message to  : " + eachmail);
                    });

                    if (listBox1.InvokeRequired)
                    {
                        listBox1.Invoke(addToListBox);
                    }
                    else
                    {
                        addToListBox();
                    }

                    failed++;
                }
                message.To.Clear();

                total++;

                Thread.Sleep(15);

                MethodInvoker updateSatatus = new MethodInvoker(delegate()
                {
                    label18.Text = total.ToString();
                    label19.Text = success.ToString();
                    label21.Text = failed.ToString();
                });

                if (this.InvokeRequired)
                {
                    this.Invoke(updateSatatus);
                }
                else
                {
                    updateSatatus();
                }
            }
            else
            {
                break;
            }
        }

        IsRunning = false;
        if (button3.InvokeRequired)
        {
            button3.Invoke(new MethodInvoker(delegate() { button3.Text = "Send"; } ));
        }
        else
        {
            button3.Text = "Send";
        }
    }

}

【讨论】:

  • 非常感谢你,你真的为我清除了很多东西
猜你喜欢
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 2013-06-10
相关资源
最近更新 更多