【问题标题】:Cross-Thread Operation? [duplicate]跨线程操作? [复制]
【发布时间】:2015-03-28 02:08:13
【问题描述】:

第一次我什至不确定 C# Cross-Thread Operation 是什么,所以看到这条调试消息从一开始就让我大吃一惊 - Cross-thread operation not valid: Control 'panel1' accessed from a thread other than the thread it was created on. 我只是想写一个文本框来显示我的程序的进度。为简洁起见,Thread.Sleep() 在下面的代码中使用。当我的代码到达panel1.Controls.Add(txt); 行时,我会收到调试消息,这是完整代码:

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
    private DateTime now = DateTime.Now;
    private int i = 0;
    TextBox txt = new TextBox();

     public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.WorkerSupportsCancellation = false;
        backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
    }

    private void button1_Click(object sender, EventArgs e)
    {
            backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
            panel1.Controls.Add(txt);
            MethodOne();
            MethodTwo();
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void MethodOne()
    {
        txt.Text = "MethodOne Process Has Begun....." + now;
        Thread.Sleep(100);
        txt.Text = "MethodOne Process Has Finished....." + now;
    }
    private void MethodTwo()
    {
        txt.Text = "MethodTwo Process Has Begun....." + now;
        Thread.Sleep(100);
        txt.Text = "MethodTwo Has Finished....." + now;
    }
}
}

如果我需要提供更多详细信息或有关如何设置我的 Windows 窗体的更多信息,请告诉我。

【问题讨论】:

  • 我不确定这会有多令人惊讶...当您创建了一个后台线程并将其设置为工作时。正如罗伯特所说 - 不能从与创建它们的线程不同的线程访问控件。

标签: c# multithreading backgroundworker


【解决方案1】:

您不能直接从 BackgroundWorker 线程访问 UI 控件。 UI 控件位于单独的线程上,因此会出现错误。这是不允许的:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    panel1.Controls.Add(txt);
    ...
}

BackgroundWorker 允许您传递一个数字(通常代表一个百分比)和另一个对象(可以是任何东西,例如您的文本)。我推荐的是:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var bgw = (BackgroundWorker)sender;

    bgw.ReportProgress(33, "MethodOne Process Has Begun.....");
    MethodOne();

    bgw.ReportProgress(66, "MethodTwo Process Has Begun.....");
    MethodTwo();

    bgw.ReportProgress(100, "All Processes Finished.");
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;

    var statusMessage = e.UserState.ToString();
    // Display statusMessage in an appropriate control, i.e. a Label
}

【讨论】:

  • 这可能也有效,因为 panel1.Controls.Add 是从 UI 线程上编组的事件中调用的。请注意,progressBar1.Value = e.ProgressPercentage; 已经可以使用了。
  • 将 panel1.Controls.Add(txt) 放在 button1_Click() 事件或 Form1() 事件下会更好(或者甚至有关系)吗?
【解决方案2】:

从不同线程在 UI 上发生的操作需要一个称为 Invoke 的特殊编组过程。如果不使用其他线程的 Invoke,则会发生错误。

【讨论】:

  • 所以问题是我试图通过 backgroundWorker1_DoWork() 过程修改我的表单?
  • 是的。您可能应该在同一个线程上修改表单;我认为修改BackgroundWorker 中的表单没有任何优势。 BackgroundWorker 用于不涉及表单的长时间运行的操作(除了已经是线程安全的进度指示器)。有关 Invoke 的代码示例,请参阅 here
猜你喜欢
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
相关资源
最近更新 更多