如何使事件处理程序异步运行?

PIC*_*ain 41 c# multithreading event-handling synchronous eventargs

我正在编写一个Visual C#程序,它在辅助线程上执行连续的操作循环.偶尔当该线程完成任务时,我希望它触发一个事件处理程序.我的程序执行此操作但触发事件处理程序时,辅助线程等待事件处理程序完成后再继续执行该线程.如何让它继续?这是我目前的结构方式......

class TestClass 
{
  private Thread SecondaryThread;
  public event EventHandler OperationFinished;

  public void StartMethod()
  {
    ...
    SecondaryThread.Start();      //start the secondary thread
  }

  private void SecondaryThreadMethod()
  {
    ...
    OperationFinished(null, new EventArgs());
    ...  //This is where the program waits for whatever operations take
         //place when OperationFinished is triggered.
  }

}
Run Code Online (Sandbox Code Playgroud)

此代码是我的某个设备的API的一部分.当触发OperationFinished事件时,我希望客户端应用程序能够执行它所需的任何操作(即相应地更新GUI),而不会导致API操作.

另外,如果我不想将任何参数传递给事件处理程序,我的语法是否正确使用OperationFinished(null, new EventArgs())

STW*_*STW 58

所以你想以阻止侦听器阻止后台线程的方式引发事件?给我几分钟的时间来举起一个例子; 这很简单:-)

我们先走了:首先要注意一个重要的事情 无论何时调用BeginInvoke都必须调用相应的EndInvoke,否则如果调用的方法抛出异常返回一个值,则ThreadPool线程永远不会被释放回池中,从而导致线程泄漏!

class TestHarness
{

    static void Main(string[] args)
    {
        var raiser = new SomeClass();

        // Emulate some event listeners
        raiser.SomeEvent += (sender, e) => { Console.WriteLine("   Received event"); };
        raiser.SomeEvent += (sender, e) =>
        {
            // Bad listener!
            Console.WriteLine("   Blocking event");
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("   Finished blocking event");
        };

        // Listener who throws an exception
        raiser.SomeEvent += (sender, e) =>
        {
            Console.WriteLine("   Received event, time to die!");
            throw new Exception();
        };

        // Raise the event, see the effects
        raiser.DoSomething();

        Console.ReadLine();
    }
}

class SomeClass
{
    public event EventHandler SomeEvent;

    public void DoSomething()
    {
        OnSomeEvent();
    }

    private void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            var eventListeners = SomeEvent.GetInvocationList();

            Console.WriteLine("Raising Event");
            for (int index = 0; index < eventListeners.Count(); index++)
            {
                var methodToInvoke = (EventHandler)eventListeners[index];
                methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, null);
            }
            Console.WriteLine("Done Raising Event");
        }
    }

    private void EndAsyncEvent(IAsyncResult iar)
    {
        var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
        var invokedMethod = (EventHandler)ar.AsyncDelegate;

        try
        {
            invokedMethod.EndInvoke(iar);
        }
        catch
        {
            // Handle any exceptions that were thrown by the invoked method
            Console.WriteLine("An event listener went kaboom!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么不直接调用多播委托,而不是使用GetInvocationList? (3认同)
  • 您将如何仅使用它异步调用事件侦听器?当然,您可以在单独的单个线程上调用*所有*侦听器-我的解决方案确实达到了在自己的线程上调用*每个*侦听器的水平-所以我可以看出它是过大的。 (2认同)

Whi*_*ght 13

使用任务并行库,现在可以执行以下操作:

Task.Factory.FromAsync( ( asyncCallback, @object ) => this.OperationFinished.BeginInvoke( this, EventArgs.Empty, asyncCallback, @object ), this.OperationFinished.EndInvoke, null );
Run Code Online (Sandbox Code Playgroud)

  • @piedar派对有点晚了,但是在多播代表调用时抛出BeginInvoke:/sf/ask/331174301/ (2认同)

Ree*_*sey 12

另外,如果我不想将任何参数传递给事件处理程序,那么使用OperationFinished(null,new EventArgs())我的语法是否正确?

通常,您可以将其称为:

OperationFinished(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)

您应该始终将对象作为发送方传递 - 它在模式中是预期的(尽管通常被忽略).EventArgs.Empty也比新的EventArgs()更好.

为了在单独的线程中触发它,最简单的选择可能只是使用线程池:

private void RaiseOperationFinished()
{
       ThreadPool.QueueUserWorkItem( new WaitCallback( (s) =>
           {
              if (this.OperationFinished != null)
                   this.OperationFinished(this, EventArgs.Empty);
           }));
}
Run Code Online (Sandbox Code Playgroud)

话虽这么说,在一个单独的线程上引发一个事件应该被彻底记录,因为它可能会导致意外的行为.

  • 今天,我会使用`Task.Run`而不是线程池. (3认同)
  • @beruic同意。这是写于2009年;) (2认同)

the*_*oop 6

尝试事件委托上的BeginInvoke和EndInvoke方法 - 这些方法立即返回,并允许您使用轮询,等待句柄或回调函数在方法完成时通知您.见这里的概述; 在您的示例中,事件是您将使用的委托


归档时间:

查看次数:

48527 次

最近记录:

10 年,6 月 前