如何在C#和TPL中并行处理事件

时间:2011-02-21 19:01:06

标签: c# events delegates parallel-processing task-parallel-library

我有一个事件,当被解雇时,订阅者执行一些可能需要一段时间才能返回的数据库代码。我想并行执行所有订阅者委托。我正在尝试使用Parallel.Foreach,但我无法提出任何建议吗?

我目前正在尝试沿着这些方向做点什么。

Parallel.ForEach(FacilitiesChanged.GetInvocationList(),某种lambda表达式)

其中,FacilitiesChanged是一个事件。

3 个答案:

答案 0 :(得分:5)

  

我有一个事件,当被解雇时,订阅者执行一些可能需要一段时间才能返回的数据库代码。我想并行执行所有订阅者委托。我正在尝试使用Parallel.Foreach,但我无法提出任何建议吗?

我建议将并行性推送到订阅者端,而不是尝试并行调用委托。

让订阅者做:

private void OnMyEvent(object sender, EventArgs e)
{
     Task.Factory.StartNew( () => 
     {
          // Your DB code logic here...
     });
}

这将导致事件异步发生。

如果你必须等待返回类型,我个人会一起避免事件。这里的问题是事件订户不会阻止固有的期望。允许事件阻止将违反预期,并导致问题。

相反,我建议使用一种控制反转形式,并通过接口而不是事件来处理。然后,这变为Observer Pattern与标准事件使用情况稍有不同的变体:

// Publisher side - add methods to add/remove "subscribers":
public void AddListener(IDatabaseOperation op)   
{
    lock(syncObj)  // thread safety may be an issue...
        this.listeners.Add(op); 
}

private void OnEvent()
{
    lock(syncObj)
    {
        Parallel.ForEach(this.listeners, l => l.DoOperation());
    }
}

如果您必须在发布方多线程调用您的调用,可以通过以下方式完成:

private void RaiseMyEvent(MyEventArgs e)
{
    var handler = this.TheEvent;

    if (handler != null)
    {
        var tasks = new List<Task>();
        foreach(var yourEvent in handler.GetInvocationList().Cast<EventHandler<MyEventArgs>())
            tasks.Add(Task.Factory.StartNew( () => yourEvent(this, e)));

        // Optionally wait here...  Removing the following makes this asynchronous
        Task.WaitAll(tasks.ToArray());
    }
}

答案 1 :(得分:0)

您可以使用Reactive Extensions在接收事件时对任务进行排队。 Reactive Extensions中有一些非常有用的功能。

执行“all all parallel”可能不是你想要的。您可以尝试手动控制Parallel {ForEach many dispatch threads使用的方式。

如果你有很多等待时间,那么你可能想要的是设计你的应用程序以使用async DB operations。请记住,如果必须使用“太多线程”,SQL服务器将会变慢。

答案 2 :(得分:0)

您可以使用Parallel.For,如果您知道在调用时要执行哪些操作。但据我了解,您希望每次事件发生时触发一些数据库操作。

所以,我会用一个队列来存储“事件请求”。第二个线程(或BackgroundWorker)可以观察队列并执行存储在那里的操作。

cherio, 克里斯