C# 异步事件的优点或替代方案(以我的多订户为例)

C# 异步事件的优点或替代方案(以我的多订户为例),c#,multithreading,events,asynchronous,C#,Multithreading,Events,Asynchronous,让我们假设我有一个类项。该项的值取自外部源。要获得最近最少的值,必须在循环中以尽可能快的速度请求该值 当然,不仅有1个项目,还有数千个。就像: while(active) { foreach(Item item in items) { item.Value = RequestValue(item.Address); } } 每件物品都可以使用多次。要获得更改,项有一个ValueChanged事件,您可以在其中订阅 假设我有很多订户,比如100或1000。每次处理都会花费时间

让我们假设我有一个类
。该项的值取自外部源。要获得最近最少的值,必须在循环中以尽可能快的速度请求该值

当然,不仅有1个
项目,还有数千个。就像:

while(active)
{
  foreach(Item item in items)
  {
    item.Value = RequestValue(item.Address);
  }
}
每件物品都可以使用多次。要获得更改,
有一个
ValueChanged
事件,您可以在其中订阅

假设我有很多订户,比如100或1000。每次处理都会花费时间,并且会延迟变量后面的循环。其思想是:使事件异步。在这种情况下,处理程序需要多长时间并不重要。但是,在这个问题上:没有人推荐异步事件(除非有良好的性能原因)

我做了一个小小的性能测试。我创建了一个
SyncItem
AsyncItem
以及一个
订户
SyncItem
正在调用
OnValueChanged
,而
AsyncItem
正在调用
新线程(()=>{OnValueChanged();})如果值已更改

我使用了一个
计时器
来更改值(引发事件)。我使用了快速测试(每50ms一次)和慢速测试(3s一次)

我使用了大量的订户
和以下处理程序:

处理程序1

//Nothing
//Slow test: Sync is faster
//Fast test: Sync is faster, Async closing window does not exit application
处理程序2

Thread.Sleep(...);
//Slow test: Sync gets slower the higher the time is, at ~ 10ms equal to async
//Fast test: Times varying, Async closing window does not exit application
处理程序3

Dispatcher.BeginInvoke(new Action(() => { control.Text = value; } ));
//Slow test: Sync is faster (async needs longer than with Handler 1)
//Fast test: both UI does not react properly, Async UI also only rarely updated
处理程序4

Dispatcher.Invoke(() => { control.Text = value; });
//Slow test: Async is faster, sync test: UI does not react properly
//Fast test: Sync UI does not react properly, Async OutOfMemoryException
我想在大多数情况下,都会使用处理程序2(任何需要一些时间的复杂方法),有时也会使用UI处理程序3或4。主要是因为处理程序2,异步事件处理似乎更快


但是,我觉得使用异步事件并不舒服。除了表演之外,还有什么好的优势吗?或者有什么替代方案?有什么可能的方法来处理这种情况?

异步事件发布可以有所帮助,但也可能引发新问题。例如,当发布服务器触发事件的速度快于订阅服务器使用事件的速度时,会发生什么情况?实际上,您已经将问题从发布者陷入困境转移到了消费者陷入排队通知的困境。如果某些使用者需要UI线程,这可能会特别有问题,这可能意味着进一步调度/排队事件。最糟糕的情况是,您可能会陷入一种有效的死锁状态,线程池和/或消息泵中充满了消息,以至于一切都慢到爬行的程度


<> P>在您的特定应用程序的实际注释中,考虑编译“更改项目”的列表是否更有意义,并在每次更新周期之后为整个集合发送单个事件。由于您有数千个项目,可能会经常更改,这可能会大大减少每个周期的事件回调次数。

我不知道性能有多好,我没有测试过。
那么您就知道首先需要做什么了。完成后,您就可以开始考虑这些选项了。@Servy我目前正在处理它。但获得替代方案来比较性能会很有帮助。就我个人而言,我会使用某种中间件(如RabbitMQ)来处理这样的发布/订阅场景。@fedab但您甚至不知道是否需要任何替代方案。如果你发现你确实有一个问题,并且你已经查看了现有的解决方案,并尝试了它们,看看它们是否有效,但它们没有,那么问一个这样的问题是合适的。事实上,你甚至不知道自己一开始是否有问题。我不确定写这篇文章时
async
/
await
是否可用,但有了TPL,订阅者现在可以将自己实现为
async void
,以允许事件发布者在处理过程中不阻塞。另外,与其创建一个专门的线程来引发事件,不如使用
ThreadPool.QueueUserWorkItem
或更简单的
Task.Run(()=>OnValueChanged())
@mhand,请注意,
async void
事件处理程序仍然会阻止发布服务器,直到它执行实际生成的
wait
。这通常意味着要么使用
Task.Run
,要么使用
Task.Delay
,要么调用一些在内部产生挂起操作系统事件的I/O方法。必须认识到,方法上的
async
限定符并不意味着“异步运行”,而是“可以异步恢复”。它创造了恢复的能力,但并不保证它会屈服。这是完全正确的。强制异步继续的另一种方法是通过
wait Task.Yield()
在事件处理程序的顶部立即屈服,强制事件处理程序的其余部分在线程池线程上运行。