在单独的线程上引发事件

Hit*_*h P 22 .net c# events multithreading asynchronous

我正在开发一个组件,需要处理实时源并以非常快的方式向听众广播数据(大约100纳秒级精度,甚至比我能做到的还要少)目前我正在筹集一个事件来自我的订阅者可以订阅的代码.但是因为在C#中,事件处理程序在引发事件的同一个线程上运行,所以引发事件的线程将被阻塞,直到所有订阅者完成事件的处理.我无法控制订阅者的代码,因此他们可能会在事件处理程序中执行任何耗时的操作,这可能会阻塞正在广播的线程.

我能做些什么才能将数据广播给其他订阅者,但仍可以快速播放这些内容?

dss*_*539 10

100 ns是一个非常艰难的目标.我相信它会深刻理解你正在做什么以及为什么要达到这种性能.

但是,异步调用事件订阅者很容易解决.这已经在这里得到了答案,还有Jon Skeet.

foreach (MyDelegate action in multicast.GetInvocationList())
{
    action.BeginInvoke(...);
}
Run Code Online (Sandbox Code Playgroud)

编辑: 我还应该提到您需要在实时操作系统上运行,以便为您的用户提供严格的性能保证.

  • +1,确实如此.使用BeginInvoke()命中100 ns确实是一个非常艰难的目标.只是上下文切换已经有几千个cpu周期:) (9认同)
  • 你的答案也没有. (2认同)

Idd*_*ian 5

看起来你正在寻找任务.以下是我为我的工作编写的扩展方法,它可以异步调用一个事件,以便每个事件处理程序都在自己的线程上.我无法对它的速度发表评论,因为这对我来说从来都不是必需的.


UPDATE

根据评论我调整它,以便只创建一个任务来调用所有订阅者

/// <summary>
/// Extension method to safely encapsulate asynchronous event calls with checks
/// </summary>
/// <param name="evnt">The event to call</param>
/// <param name="sender">The sender of the event</param>
/// <param name="args">The arguments for the event</param>
/// <param name="object">The state information that is passed to the callback method</param>
/// <remarks>
/// This method safely calls the each event handler attached to the event. This method uses <see cref="System.Threading.Tasks"/> to
/// asynchronously call invoke without any exception handling. As such, if any of the event handlers throw exceptions the application will
/// most likely crash when the task is collected. This is an explicit decision since it is really in the hands of the event handler
/// creators to make sure they handle issues that occur do to their code. There isn't really a way for the event raiser to know
/// what is going on.
/// </remarks>
[System.Diagnostics.DebuggerStepThrough]
public static void AsyncSafeInvoke( this EventHandler evnt, object sender, EventArgs args )
{
    // Used to make a temporary copy of the event to avoid possibility of
    // a race condition if the last subscriber unsubscribes
    // immediately after the null check and before the event is raised.
    EventHandler handler = evnt;
    if (handler != null)
    {
        // Manually calling all event handlers so that we could capture and aggregate all the
        // exceptions that are thrown by any of the event handlers attached to this event.  
        var invocationList = handler.GetInvocationList();

        Task.Factory.StartNew(() =>
        {
            foreach (EventHandler h in invocationList)
            {
                // Explicitly not catching any exceptions. While there are several possibilities for handling these 
                // exceptions, such as a callback, the correct place to handle the exception is in the event handler.
                h.Invoke(sender, args);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我的本能是他确实想要调用每个处理程序异步,以便1个不良消费者不会将消息延迟到其他消费者. (2认同)