繁体   English   中英

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

[英]how can I handle an event in parallel in C# and TPL

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

我目前正在尝试按照这些方式做一些事情。

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

其中,设施变更是一个事件。

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

我建议将并行性推入订阅者一侧,而不是尝试并行调用委托。

只需让订阅者执行以下操作:

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

这将导致事件异步发生。

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

相反,我建议使用控制反转的形式,并通过接口而不是事件来处理它。 这将成为观察者模式与标准事件用法有些不同的变体:

// 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());
    }
}

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

执行“全部并行”可能不是您想要的。 您可以改为尝试手动控制 Parallel.ForEach 使用的分派线程数

如果您有很多等待时间,那么您可能想要设计应用程序以使用异步数据库操作 请记住,如果 SQL 服务器必须使用“太多线程”,它会变慢。

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

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

切里奥,克里斯

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM