C# 如何在Winform中使用多线程?

C# 如何在Winform中使用多线程?,c#,.net,winforms,multithreading,C#,.net,Winforms,Multithreading,我是个多线程新手。我有一个winform,它有一个标签和一个进度条 我想显示处理结果。首先,我使用Application.DoEvents()方法。但是,我发现表格冻结了 然后我在MSDN上读了一些关于多线程的文章 第二,我使用后台人员来完成这项工作 this.bwForm.DoWork += (o, arg) => { DoConvert(); }; this.bwForm.RunWorkerAsync(); 表单不会冻结,因此我可以在处理时拖动/拖动。不幸的是,它抛出了一个Inval

我是个多线程新手。我有一个winform,它有一个标签和一个进度条

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

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

第二,我使用后台人员来完成这项工作

this.bwForm.DoWork += (o, arg) => { DoConvert(); };
this.bwForm.RunWorkerAsync();
表单不会冻结,因此我可以在处理时拖动/拖动。不幸的是,它抛出了一个InvalidOperationException。所以我必须用这个<代码>Control.CheckForIllegalCrossThreadCalls=false我确保这不是最终解决方案

专家们,你们有什么建议吗

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

Edit2:
我在DoWork()中使用delegate和invoke,它不会抛出exeption。但是这个表格又冻结了。如何使表单在处理时可以拖放?

您应该使用。有关详细信息,请参见。

调用UI线程。例如:

void SetControlText(Control control, string text)
{
    if (control.InvokeRequired)
        control.Invoke(SetControlText(control, text));
    else
        control.Text = text;
}

这样做,创建一个新线程

         Thread loginThread = new Thread(new ThreadStart(DoWork));

         loginThread.Start();
在ThreadStart()中,传递要执行的方法。如果在该方法中要更改某些控件属性,请创建一个委托,并将其指向将在其中写入控件更改属性的方法

         public delegate void DoWorkDelegate(ChangeControlsProperties);         
调用控件属性,声明一个方法,并在其中定义控件的新属性

         public void UpdateForm()
         {
             // change controls properties over here
         }
然后用这种方式将委托指向该方法

         InvokeUIControlDelegate invokeDelegate = new InvokeUIControlDelegate(UpdateForm);
那么当你想在任何地方改变属性的时候就叫这个

         this.Invoke(invokeDelegate);

希望此代码片段对您有所帮助!:)

避免调用
应用程序.DoEvents
。通常情况下,这会导致更多的问题,而不是解决问题。这通常被认为是一种不好的做法,因为存在更好的替代方案来保持UI的消息传递

避免更改设置
CheckForIllegalCrossThreadCalls=false
。这并不能解决任何问题。这只会掩盖问题。问题是您试图从主UI线程以外的线程访问UI元素。如果禁用
CheckForIllegalCrossThreadCalls
,那么您将不再获得异常,而是应用程序将以不可预测的方式异常失败


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

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

private BackgroundWorker bwForm;
private ProgressBar progressBar;
在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();

请看这里:

编辑

所以我不明白你的问题,我以为是关于在工作中显示进度条的。 如果是关于工作结果的,请使用e。导致BwForm_DoWork事件。 为已完成的事件和管理结果添加新的事件处理程序:

this.bwForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwForm_RunWorkerCompleted);


最干净的方法是像您一样使用
BackGroundWorker

你只是错过了一些要点:

  • 您不能在
    DoWork
    事件处理程序中访问表单元素,因为这会导致跨线程方法调用,这应该在
    ProgressChanged
    事件处理程序中完成
  • 默认情况下,
    BackGroundWorker
    将不允许报告进度,也不允许取消操作。在代码中添加
    BackGroundWorker
    时,如果要调用
    BackGroundWorker
    ReportProgress
    方法,则必须将该
    BackGroundWorker
    WorkerReportsProgress
    属性设置为
    true
  • 如果您还需要允许用户取消操作,请将
    WorkerSupportsCancellation
    设置为true,并在
    DoWork
    事件处理程序的循环中检查
    BackGroundWorker
    中名为
    CancellationPending
    的属性

  • 我希望我能提供帮助

    您所展示的代码存在一些基本问题。正如其他人提到的,
    Application.DoEvents()
    不会从后台线程执行您想要的操作。将缓慢的后台处理分离为
    BackgroundWorker
    ,并在UI线程中更新进度条。您正在从后台线程调用
    progressBar1.Value++
    ,这是错误的

    永远不要调用Control.CheckForIllegalCrossThreadCalls=false,这只会隐藏错误

    如果要从后台线程更新进度条,则必须实现
    ProgressChanged
    处理程序;你没有那样做


    如果您需要实现
    BackgroundWorker
    的示例,请参阅code项目中的文章

    再加上自由和平的解决方案,他是对的。此解决方案是线程安全的,这正是您想要的。您只需要使用
    BeginInvoke
    而不是
    Invoke

    void SetControlText(Control control, string text)
    {
        control.BeginInvoke(
            new MethodInvoker(() =>
            {
                control.Text = text;
            })
        );
    }
    
    上述修复是最简单、最干净的


    希望这有帮助。:)

    我想说,编写线程安全的应用程序是更难理解和正确执行的事情之一。你真的需要好好读一读。如果你从一本书中学习了C#,那么回头看看是否没有关于多线程的章节。我从安德鲁·特罗尔森(Andrew Troelsen)的书中了解到(最新一本是Pro C#2005和.NET 2.0平台),他直到第14章才触及这个话题(因此当时很多人已经停止阅读)。我来自一个嵌入式编程背景,其中并发性和原子性也很重要,所以这不是一个.NET或Windows特定的问题

    这里的很多帖子都有详细介绍
    this.bwForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwForm_RunWorkerCompleted);
    
    private void BwForm_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        YourResultStruct result = e.Result as YourResultStruct;
        if (e.Error != null && result != null)
        {
            // Handle result here
        }
    }
    
    void SetControlText(Control control, string text)
    {
        control.BeginInvoke(
            new MethodInvoker(() =>
            {
                control.Text = text;
            })
        );
    }
    
    intVal++; //This is not thread safe 
    int newVal = Interlocked.Increment(ref intVal); //This is thread safe