如何在Winform中使用多线程?

Jus*_*tin 14 .net c# multithreading winforms

我是多线程的新手.我有一个带有标签和进度条的winform.

我想显示处理结果.首先,我使用Application.DoEvents()方法.但是,我发现表格很冷.

然后我在MSDN上阅读了一些关于多线程的文章.

其次,我使用BackgroundWorker来做到这一点.

this.bwForm.DoWork += (o, arg) => { DoConvert(); };
this.bwForm.RunWorkerAsync();
Run Code Online (Sandbox Code Playgroud)

表格不会冻结,我可以在处理时拖动/ drog.不幸的是,它会抛出InvalidOperationException.所以我必须使用它.Control.CheckForIllegalCrossThreadCalls = false;我确保这不是最终的解决方案.

专家,您有什么建议吗?

编辑: 当我调用listview时抛出InvalidOperationException.此代码位于DoWork()中.

          foreach (ListViewItem item in this.listView1.Items)
            {
                //........some operation
                 lbFilesCount.Text = string.Format("{0} files", listView1.Items.Count);
                 progressBar1.Value++;
            }
Run Code Online (Sandbox Code Playgroud)

Edit2: 我在DoWork()中使用委托并调用它并不会抛出异常.但形式再次冻结.怎么做才能使表格在处理时可以放置/悬垂?

fox*_*oxy 9

调用UI线程.例如:

void SetControlText(Control control, string text)
{
    if (control.InvokeRequired)
        control.Invoke(SetControlText(control, text));
    else
        control.Text = text;
}
Run Code Online (Sandbox Code Playgroud)


Vin*_*nce 9

您只能从UI线程(WinForm)设置进度条用户控件属性.使用BackgroundWorker最简单的方法是使用ProgressChanged事件:

private BackgroundWorker bwForm;
private ProgressBar progressBar;
Run Code Online (Sandbox Code Playgroud)

在WinForm构造函数中:

this.progressBar = new ProgressBar();
this.progressBar.Maximum = 100;
this.bwForm = new BackgroundWorker();
this.bwForm.DoWork += new DoWorkEventHandler(this.BwForm_DoWork);
this.bwForm.ProgressChanged += new ProgressChangedEventHandler(this.BwForm_ProgressChanged);
this.bwForm.RunWorkerAsync();
Run Code Online (Sandbox Code Playgroud)

...

void BwForm_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bgw = sender as BackgroundWorker;
    // Your DoConvert code here
    // ...          
    int percent = 0;
    bgw.ReportProgress(percent);
    // ...
}

void BwForm_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.progressBar.Value = e.ProgressPercentage;
}
Run Code Online (Sandbox Code Playgroud)

请看:http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

编辑

所以我不明白你的问题,我认为这是关于在工作期间显示进度条的进展.如果是关于工作的结果,请在BwForm_DoWork事件中使用e.Result.为已完成的事件添加新的事件处理程序并管理结果:

this.bwForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwForm_RunWorkerCompleted);
Run Code Online (Sandbox Code Playgroud)

...

private void BwForm_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    YourResultStruct result = e.Result as YourResultStruct;
    if (e.Error != null && result != null)
    {
        // Handle result here
    }
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*eon 6

避免打电话Application.DoEvents.通常情况下,这会导致比解决的问题更多的问题.它通常被认为是不好的做法,因为存在更好的替代方案,以保持UI抽取消息.

避免改变设置CheckForIllegalCrossThreadCalls = false.这不能解决任何问题.它只掩盖了问题.问题是您正在尝试从主UI线程以外的线程访问UI元素.如果你禁用,CheckForIllegalCrossThreadCalls那么你将不再获得异常,而是你的应用程序将无法预测和惊人地失败.

DoWork事件处理程序中,您需要定期调用ReportProgress.从您Form那里您将要订阅该ProgressChanged活动.从ProgressChanged事件处理程序中访问UI元素是安全的,因为它会自动封送到UI线程中.


Zru*_*uty 5

您应该使用Control.Invoke().有关详细信息,请参阅此问题.


Maz*_*eri 5

最干净的方式是BackGroundWorker像你一样使用.

你错过了一些观点:

  1. 您无法在DoWork事件处理程序中访问表单元素,因为这会使跨线程方法调用,这应该在ProgressChanged事件处理程序中完成.
  2. 默认情况下,a BackGroundWorker不允许报告进度,也不允许取消操作.当您添加BackGroundWorker到您的代码,你必须设置WorkerReportsProgress的那个属性BackGroundWorkertrue,如果你想打电话给ReportProgress你的方法BackGroundWorker.
  3. 如果您还需要允许用户取消操作,请设置WorkerSupportsCancellation为true并在DoWork事件处理程序的循环中检查CancellationPending您的名称中指定的属性BackGroundWorker

我希望我帮忙