【问题标题】:Raise event from second thread to main thread, without forms [duplicate]从第二个线程引发事件到主线程,没有表单[重复]
【发布时间】:2017-05-17 14:35:56
【问题描述】:

我正在编写一个触发事件的库。此库启动第二个线程,该线程连接到服务器并侦听消息(阻塞调用,第二个线程的原因)。

public virtual event LogEventHandler EntryReceived;

protected virtual void ReceiveEntry(ILogEntry entry)
{
    if (EntryReceived != null)
        EntryReceived(this, new LogEventArgs() { Entry = entry });
}

当从服务器接收到消息时,它会触发一个事件:

ReceiveEntry(entry);

我希望最终开发人员不必在他的事件处理程序中考虑 InvokeRequired/Invoke sn-p。我如何确保在“父”线程(我知道它与实例化我的类的线程相同)上触发我的事件?

【问题讨论】:

    标签: c# multithreading events invoke


    【解决方案1】:

    为此目的,有一些 winforms 元素名为SynchronizingObject 的属性。此属性的类型为ISynchronizeInvoke,它具有在 UI 线程中执行调用所需的方法。

    在您的代码中,您检查此属性是否为空,如果已设置,则使用它:

    var sync = this.SynchronizingObject;
    
    if (sync != null && sync.InvokeRequired)
        sync.BeginInvoke(new Action(()=> ReceiveEntry(entry), null);
    else                        
       ReceiveEntry(entry); 
    

    库的用户只需将控件或表单放入该属性:

    private MyLibraryClass _MyLibraryClass;
    
    public Form1()
    {
        InitializeComponent();
    
        _MyLibraryClass = new MyLibraryClass();
        _MyLibraryClass.SynchronizingObject = this;
        _MyLibraryClass.EntryReceived += OnEntryReceived;
    }
    
    private void OnEntryReceived(object sender, LogEventArgs e)
    {
        myTextBox.Text += e.Entry.Message;
    }
    

    【讨论】:

      【解决方案2】:

      如果在对象构造期间您捕获了SynchronizationContext,您将能够在该上下文中发送事件(如果有),如果没有上下文,那么您的类是在一个线程上构造的,它不关心哪个线程将用于引发事件。这比ISynchronizeInvoke 好,因为SynchronizationContext 将适用于WinForms、ASP.NET 和WPF,而ISynchronizeInvoke 仅适用于WinForms。

      C# 6 版本

      public class Example
      {
          private SynchronizationContext _context;
      
          public Example()
          {
              var existingContext = SynchronizationContext.Current;
              _context = existingContext?.CreateCopy() ?? new SynchronizationContext();
          }
      
      
          public virtual event LogEventHandler EntryReceived;
      
          protected virtual void ReceiveEntry(ILogEntry entry)
          {
              _context.Send(ContextCallback, entry);
          }
      
          private void ContextCallback(object entry)
          {
              EntryReceived?.Invoke(this, new LogEventArgs() { Entry = (ILogEntry)entry });
          }
      }
      

      C# 5 及更低版本

      public class Example
      {
          private SynchronizationContext _context;
      
          public Example()
          {
              var existingContext = SynchronizationContext.Current;
              _context = existingContext != null ? existingContext.CreateCopy() : new SynchronizationContext();
          }
      
      
          public virtual event LogEventHandler EntryReceived;
      
          protected virtual void ReceiveEntry(ILogEntry entry)
          {
              _context.Send(ContextCallback, entry);
          }
      
          private void ContextCallback(object entry)
          {
              var temp = EntryReceived;
              if (temp != null)
              {
                  temp(this, new LogEventArgs() {Entry = (ILogEntry)entry});
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-22
        • 2021-12-09
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多