C#事件:如何以并行方式处理事件

C#事件:如何以并行方式处理事件,c#,performance,event-handling,delegates,parallel-processing,C#,Performance,Event Handling,Delegates,Parallel Processing,我有一个我想以并行方式处理的事件。我的想法是将每个回调添加到ThreadPool,有效地让每个注册ThreadPool处理的事件的方法 我的试用代码如下所示: Delegate[] delegates = myEvent.GetInvocationList(); IAsyncResult[] results = new IAsyncResult[ delegates.Count<Delegate>() ]; for ( int i = 0; i < delegates.Cou

我有一个我想以并行方式处理的事件。我的想法是将每个回调添加到ThreadPool,有效地让每个注册ThreadPool处理的事件的方法

我的试用代码如下所示:

Delegate[] delegates = myEvent.GetInvocationList();
IAsyncResult[] results = new IAsyncResult[ delegates.Count<Delegate>() ];

for ( int i = 0; i < delegates.Count<Delegate>(); i++ )
{
    IAsyncResult result = ( ( TestDelegate )delegates[ i ] ).BeginInvoke( "BeginInvoke/EndInvoke", null, null );
    results[ i ] = result;
}

for ( int i = 0; i < delegates.Length; i++ )
{
    ( ( TestDelegate )delegates[ i ] ).EndInvoke( results[ i ] );
}
Delegate[]delegates=myEvent.GetInvocationList();
IAsyncResult[]结果=新的IAsyncResult[delegates.Count()];
对于(int i=0;i
这只是为了玩玩,因为我很好奇怎么做。我相信有更好的办法。我不喜欢用Func创建包含lambda的WaitCallback。另外,与直接调用委托相比,DynamicVoke相当慢。我怀疑这种处理事件的方式是否比按顺序处理快

我的问题是:如何以并行方式处理事件,最好使用ThreadPool

由于我通常使用Mono,.NET4.0或任务并行库,因此两者都不是选项

谢谢大家!

编辑: -正确的例子感谢耳塞的回答。
-更新的试用代码

您似乎在代码段中执行了两次异步启动

首先,在委托上调用BeginInvoke-这将对工作项进行排队,以便线程池将执行委托

然后在该委托内,使用QueueUserWorkItem。。。将另一个工作项排队,以便线程池将执行真正的委托


这意味着,当您从外部委托返回IAsyncResult(以及等待句柄)时,它将在第二个工作项排队时发出完成信号,而不是在它完成执行时发出完成信号。

我会选择使用DynamicMethod(LCG)和状态对象的方法,该对象携带参数并跟踪调用(以便您可以等待它们完成)

代码: 这样做应该可以(虽然还没有完全测试,但在某些情况下可能会抛出一些令人讨厌的异常):

注意事项:

ParallelInvoke.Invoke(yourDelegate, arguments);
  • 不处理事件处理程序中的异常(但IL代码有一个finally来减少计数器,以便方法正确结束),这可能会造成问题。也可以捕获并传输IL代码中的异常

  • 不执行继承以外的隐式转换(如int到double),并将引发异常

  • 所使用的同步技术不分配操作系统等待句柄,这通常有助于提高性能。有关监视器工作的说明,请参阅

  • 经过一些性能测试后,这种方法似乎比对委托使用“本机”BeginInvoke/EndInvoke调用的任何方法(至少在MS CLR中)的扩展性都要好得多


如果委托的类型已知,则可以直接调用其BeginInvoke an将IAsyncResults存储在数组中以等待并结束调用。请注意,您应该调用EndInvoke以等待。代码依赖EndInvoke等待调用完成的事实,因此不需要WaitAll(请注意,这样我就不用它了)

这是一个代码示例,同时也是不同方法的一个简单基准:

public static class MainClass {
    private delegate void TestDelegate(string x);

    private static void A(string x) {}

    private static void Invoke(TestDelegate test, string s) {
        Delegate[] delegates = test.GetInvocationList();
        IAsyncResult[] results = new IAsyncResult[delegates.Length];
        for (int i = 0; i < delegates.Length; i++) {
            results[i] = ((TestDelegate)delegates[i]).BeginInvoke("string", null, null);
        }
        for (int i = 0; i < delegates.Length; i++) {
            ((TestDelegate)delegates[i]).EndInvoke(results[i]);
        }
    }

    public static void Main(string[] args) {
        Console.WriteLine("Warm-up call");
        TestDelegate test = A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A; // 10 times in the invocation list
        ParallelInvoke.Invoke(test, "string"); // warm-up
        Stopwatch sw = new Stopwatch();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Profiling calls");
        sw.Start();
        for (int i = 0; i < 100000; i++) {
            // ParallelInvoke.Invoke(test, "string"); // profiling ParallelInvoke
            Invoke(test, "string"); // profiling native BeginInvoke/EndInvoke
        }
        sw.Stop();
        Console.WriteLine("Done in {0} ms", sw.ElapsedMilliseconds);
        Console.ReadKey(true);
    }
}
公共静态类MainClass{
私有委托void TestDelegate(字符串x);
私有静态void A(字符串x){}
私有静态void调用(TestDelegate测试,字符串s){
Delegate[]delegates=test.GetInvocationList();
IAsyncResult[]结果=新的IAsyncResult[delegates.Length];
for(int i=0;i

在我非常旧的笔记本电脑上,使用BeginInvoke/EndInvoke需要95553毫秒,而使用我的ParallelInvoke方法(ms.NET 3.5)需要9038毫秒。因此,与ParallelInvoke解决方案相比,这种方法的扩展性不好。

您这样做是为了提高性能吗


这只有在允许多个硬件并行工作的情况下才能起作用,并且会花费您的进程切换开销。

您是对的,一开始我没有想到这里,在玩的时候它会悄悄出现。我会纠正这个例子!这将非常有趣,期待您的示例。发布了一些代码,但是我只是做了一个快速的测试。但是似乎很有效。谢谢你的代码!它似乎有效,而且很高兴它比阅读容易得多;-)我运行了一个小的性能测试,将你的代码与事件的直接调用进行比较,然后与我在
public static class MainClass {
    private delegate void TestDelegate(string x);

    private static void A(string x) {}

    private static void Invoke(TestDelegate test, string s) {
        Delegate[] delegates = test.GetInvocationList();
        IAsyncResult[] results = new IAsyncResult[delegates.Length];
        for (int i = 0; i < delegates.Length; i++) {
            results[i] = ((TestDelegate)delegates[i]).BeginInvoke("string", null, null);
        }
        for (int i = 0; i < delegates.Length; i++) {
            ((TestDelegate)delegates[i]).EndInvoke(results[i]);
        }
    }

    public static void Main(string[] args) {
        Console.WriteLine("Warm-up call");
        TestDelegate test = A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A;
        test += A; // 10 times in the invocation list
        ParallelInvoke.Invoke(test, "string"); // warm-up
        Stopwatch sw = new Stopwatch();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Profiling calls");
        sw.Start();
        for (int i = 0; i < 100000; i++) {
            // ParallelInvoke.Invoke(test, "string"); // profiling ParallelInvoke
            Invoke(test, "string"); // profiling native BeginInvoke/EndInvoke
        }
        sw.Stop();
        Console.WriteLine("Done in {0} ms", sw.ElapsedMilliseconds);
        Console.ReadKey(true);
    }
}