调用跨线程事件的最简洁方法

Nic*_*ick 77 c# events multithreading

我发现.NET事件模型是这样的,我经常会在一个线程上引发事件并在另一个线程上侦听它.我想知道将后台线程中的事件编组到我的UI线程的最简洁方法是什么.

根据社区建议,我用过这个:

// earlier in the code
mCoolObject.CoolEvent+= 
           new CoolObjectEventHandler(mCoolObject_CoolEvent);
// then
private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    if (InvokeRequired)
    {
        CoolObjectEventHandler cb =
            new CoolObjectEventHandler(
                mCoolObject_CoolEvent);
        Invoke(cb, new object[] { sender, args });
        return;
    }
    // do the dirty work of my method here
}
Run Code Online (Sandbox Code Playgroud)

Dom*_*nic 43

我在网上有一些代码.它比其他建议好得多; 绝对检查一下.

样品用法:

private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    // You could use "() =>" in place of "delegate"; it's a style choice.
    this.Invoke(delegate
    {
        // Do the dirty work of my method here.
    });
}
Run Code Online (Sandbox Code Playgroud)


Sha*_*tin 27

几点意见:

  • 不要在类似的代码中显式创建简单的委托,除非你是pre-2.0,所以你可以使用:
   BeginInvoke(new EventHandler<CoolObjectEventArgs>(mCoolObject_CoolEvent), 
               sender, 
               args);
Run Code Online (Sandbox Code Playgroud)
  • 此外,您不需要创建和填充对象数组,因为args参数是"params"类型,因此您只需传入列表即可.

  • 我可能会赞成Invoke,BeginInvoke因为后者将导致代码被异步调用,这可能会或可能不是你所追求的,但会使后续异常难以传播而不需要调用EndInvoke.会发生什么事情,你的应用程序将最终得到一个TargetInvocationException.


Kon*_*lph 10

我避免冗余的代表声明.

private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args)
{
    if (InvokeRequired)
    {
        Invoke(new Action<object, CoolObjectEventArgs>(mCoolObject_CoolEvent), sender, args);
        return;
    }
    // do the dirty work of my method here
}
Run Code Online (Sandbox Code Playgroud)

对于非事件,您可以使用System.Windows.Forms.MethodInvoker委托或System.Action.

编辑:此外,每个事件都有一个相应的EventHandler代表,所以根本不需要重新声明一个.


小智 5

我出于自己的目的制作了以下“通用”跨线程调用类,但我认为值得分享:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace CrossThreadCalls
{
  public static class clsCrossThreadCalls
  {
    private delegate void SetAnyPropertyCallBack(Control c, string Property, object Value);
    public static void SetAnyProperty(Control c, string Property, object Value)
    {
      if (c.GetType().GetProperty(Property) != null)
      {
        //The given property exists
        if (c.InvokeRequired)
        {
          SetAnyPropertyCallBack d = new SetAnyPropertyCallBack(SetAnyProperty);
          c.BeginInvoke(d, c, Property, Value);
        }
        else
        {
          c.GetType().GetProperty(Property).SetValue(c, Value, null);
        }
      }
    }

    private delegate void SetTextPropertyCallBack(Control c, string Value);
    public static void SetTextProperty(Control c, string Value)
    {
      if (c.InvokeRequired)
      {
        SetTextPropertyCallBack d = new SetTextPropertyCallBack(SetTextProperty);
        c.BeginInvoke(d, c, Value);
      }
      else
      {
        c.Text = Value;
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

你可以简单地从另一个线程使用 SetAnyProperty() :

CrossThreadCalls.clsCrossThreadCalls.SetAnyProperty(lb_Speed, "Text", KvaserCanReader.GetSpeed.ToString());
Run Code Online (Sandbox Code Playgroud)

在这个例子中,上面的 KvaserCanReader 类运行它自己的线程并调用以设置主窗体上 lb_Speed 标签的 text 属性。