Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/visual-studio-code/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#多线程-使用后台事件更新GUI_C#_Multithreading - Fatal编程技术网

C#多线程-使用后台事件更新GUI

C#多线程-使用后台事件更新GUI,c#,multithreading,C#,Multithreading,我是C#和多线程的新手,所以如果这是一个重复的问题,我很抱歉,但作为一个新手,我的问题似乎与我读过的其他问题略有不同 我的GUI在一个(主)线程中运行。它调用一个在单独线程中运行的后台任务(在dll中,我也在编写)。dll不了解GUI(即,它不能引用GUI类) 现在,假设我想根据dll线程的状态更新GUI上的进度条->我正在dll中创建一个事件,每X%触发一次,GUI将订阅该事件。触发事件时,GUI将更新进度条 我的问题是: 创建事件的方法是最好的方法吗(请记住dll不能引用GUI) 我如何确保

我是C#和多线程的新手,所以如果这是一个重复的问题,我很抱歉,但作为一个新手,我的问题似乎与我读过的其他问题略有不同

我的GUI在一个(主)线程中运行。它调用一个在单独线程中运行的后台任务(在dll中,我也在编写)。dll不了解GUI(即,它不能引用GUI类)

现在,假设我想根据dll线程的状态更新GUI上的进度条->我正在dll中创建一个事件,每X%触发一次,GUI将订阅该事件。触发事件时,GUI将更新进度条

我的问题是:

  • 创建事件的方法是最好的方法吗(请记住dll不能引用GUI)
  • 我如何确保我的上述方法是“事件安全的”?我应该通过事件中的进度百分比来保证线程安全,还是还有更多
  • 更新GUI时是否需要使用Invoke?我看到一篇帖子建议我这么做,但我不明白为什么,因为更新的酒吧是在GUI线程中完成的 希望你能为我澄清这一点


    谢谢

    查看BackgroundWorker课程。听起来很适合你的场景


    MSDN上的此链接解释了如何使用它:

    要回答(3),您需要使用Invoke。事件处理程序将从后台线程而不是GUI线程运行。

    请记住,在大多数情况下,从后台任务引发的事件也将在后台线程上运行。根本不会自动进行线程上下文切换

    要理解为什么,你必须考虑一个事件是什么;只是某种类型的
    委托
    对象。您正在从主线程设置该事件的委托。。。但该委托实际上将在后台线程中调用,在触发事件的代码中


    是的;您需要确保将事件从事件处理程序中转移到GUI线程上运行。

    如果您派生线程,则需要创建一个委托,该委托可以使用适当的参数安全地调用主线程

    delegate void UpdateDelegate(int val)
    void Update(int val)
    {
      if(this.InvokeRequired())
      {
         Invoke(new UpdateDeleage(Update),new object[] {val});
         return;
      }
      this.MyProgressBar.Value = val;
    }
    
    从单独的线程调用Update,就像从主线程调用Update一样。一旦线程确定需要调用主线程来传递值,它将使用您传递的参数和委托来调用它。否则,它将跳过该块并设置您的值

    e、 g


    1.-我一直使用这种方法,是的,它会起作用

    2.-只需将int传递给事件处理程序,即可安全读取变量。但是,当您从代码触发事件时,请执行以下操作

    private void UpdatePercentage(int a)
    {
        var myEvent = PercentageUpdatedEvent
        if(myEvent != null)
             myEvent(this, new ProgressBarEventArgs(a));
    }
    
    这样做的原因是,如果在空检查和调用之间取消预订事件,则不会出现异常

    3.-正如其他人提到的,您需要调用Invoke,因为事件将在dll的线程上运行。但是,对于控件,在没有EndEnvoike的情况下调用BeginInvoke是合法的,因此调用在dll的线程上是非阻塞的

    这是我经常使用的模式

    private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
    {
        if(progressBar.InvokeRequired)
            progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
        else
        {
            progressBar.Value = e.Value;
        }
    }
    
    private myClass\u onPercentageUpdateEvent(对象a,ProgressBarEventArgs e)
    {
    if(progressBar.invokererequired)
    progressBar.BeginInvoke((操作)myCless_on PercentageUpdateEvent,a,e);
    其他的
    {
    progressBar.Value=e.Value;
    }
    }
    
    对于这个问题,我有几种不同的方法,每种方法都有各自的优缺点。总之,我建议使用
    任务
    类。

    你所说的“事件安全”是什么意思?请参阅
    BackgroundWorker
    类的评论:我只想感谢所有的回复。他们都帮助回答了我的问题。很高兴看到这样的帮助!BackgroundWorker试图帮助您解决运行后台操作的任务,而不需要任何关于线程和UI如何工作的实际知识,但如果您确实了解如何引发事件以及如何使用Dispatcher,则不会提供太多帮助。我强烈建议任何想做多线程代码的人,至少要尝试去理解在一个抽象(比如后台工作者)的框架下发生的事情。我同意理解框架下发生的事情是很重要的,但我认为,如果你不了解引擎盖下发生的事情,那么BackgroundWorker可能是实现它的最安全的方法。这真的很有帮助。非常感谢。它最后点击(可能对你们所有人来说都很明显),即使事件处理程序是在GUI类中编写的,它仍然在dll线程中运行,就像它被“调用”的地方一样!现在我明白了为什么我们需要调用,这就澄清了这一点。我同意其他帖子的观点,我需要理解它为什么/如何工作,而不是使用BackgroundWorker等。这些帖子明确了这一点:“但实际上,在触发事件的代码中,该委托将在后台线程中被调用。”
    private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
    {
        if(progressBar.InvokeRequired)
            progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
        else
        {
            progressBar.Value = e.Value;
        }
    }