Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 事件与收益_C#_Multithreading_Events_Yield - Fatal编程技术网

C# 事件与收益

C# 事件与收益,c#,multithreading,events,yield,C#,Multithreading,Events,Yield,我有一个多线程应用程序,它为几个硬件工具生成线程。每个线程基本上是一个无限循环(在应用程序的生命周期内),它轮询硬件以获取新数据,并在每次收集新数据时激活一个事件(传递数据)。有一个侦听器类可以合并所有这些工具,执行一些计算,并使用此计算触发一个新事件 但是,我想知道,既然只有一个侦听器,那么最好从这些工具中公开一个IEnumerable方法,并使用yield return来返回数据,而不是触发事件 我想看看是否有人知道这两种方法的区别。特别是,我正在寻找最佳可靠性、最佳暂停/取消操作能力、最佳

我有一个多线程应用程序,它为几个硬件工具生成线程。每个线程基本上是一个无限循环(在应用程序的生命周期内),它轮询硬件以获取新数据,并在每次收集新数据时激活一个事件(传递数据)。有一个侦听器类可以合并所有这些工具,执行一些计算,并使用此计算触发一个新事件

但是,我想知道,既然只有一个侦听器,那么最好从这些工具中公开一个
IEnumerable
方法,并使用
yield return
来返回数据,而不是触发事件

我想看看是否有人知道这两种方法的区别。特别是,我正在寻找最佳可靠性、最佳暂停/取消操作能力、最佳线程目的、一般安全性等


另外,使用第二种方法是否仍然可以在单独的线程上运行
IEnumerable
循环?这些工具中的许多都有一定程度的CPU限制,因此确保每一个都在不同的线程上是至关重要的。

这是一个千钧一发的机会,但我认为在这种情况下我会坚持使用事件模型,主要决策者是未来的维护程序员不太可能理解收益率概念。此外,屈服意味着处理每个硬件请求的代码与生成处理请求的代码位于同一线程中。这很糟糕,因为这可能意味着您的硬件必须等待消费者代码


说到消费者,另一个选择是生产者/消费者队列。您的乐器可以全部推入同一队列,您的单个监听器可以从中弹出,然后从中执行任何操作。

Stephen Toub讲述了一个阻塞队列,它使用
yield
关键字将
IEnumerable
实现为一个无限循环。您的工作线程可以在新数据点出现时将其排队,而计算线程可以使用带有阻塞语义的
foreach
循环将其排队

推和拉有一个非常根本的区别。从仪器接口的角度来看,拉动模型(产量)更难实现。因为在客户端代码准备好提取之前,您必须存储数据。当您推送时,客户端可以存储,也可以不存储,因为它认为有必要


但多线程场景中的大多数实际实现需要处理呈现数据所需的不可避免的线程上下文切换中的开销。这通常是通过使用线程安全的有界队列来实现的。

这听起来像是反应式扩展的一个非常好的用例。它有一点学习曲线,但简而言之,IObservable是IEnumerable的对偶。当IEnumerable要求您从中提取时,IObservable将其值推送到观察者。几乎在任何时候你需要在你的枚举器中阻塞,这是一个很好的迹象,你应该反转模式并使用推模型。事件是一种方式,但IObservable具有更大的灵活性,因为它是可组合的,并且支持线程

instrument.DataEvents
          .Where(x => x.SomeProperty == something)
          .BufferWithTime( TimeSpan.FromSeconds(1) )
          .Subscribe( x => DoSomethingWith(x) );
在上面的示例中,每当主题(工具)生成具有匹配SomeProperty的DataEvent,并且它将事件缓冲到持续1秒的批中时,就会调用DoSomethingWith(x)


您还可以做很多事情,比如合并其他主题生成的事件,或者将通知定向到UI线程上,等等。不幸的是,文档目前非常薄弱,但是有一些很好的相关信息。(虽然他的帖子几乎只提到了JavaScript的反应式扩展,但它也几乎完全适用于.NET的反应式扩展。)

我认为事件方法和
收益方法在性能方面没有太大区别<代码>产量
是延迟计算的,因此它留下了一个机会来通知生产线程停止。如果您的代码经过深思熟虑的记录,那么维护也应该是一次清洗

我的首选是第三个选项,即使用回调方法而不是事件(即使两者都涉及委托)。生产者每次拥有数据时都会调用回调。回调可以返回值,因此消费者可以在生产者每次签入数据时发出停止或继续的信号

如果您有大量数据,这种方法可以为您提供优化性能的位置。在回调中,锁定一个中立对象,并将传入数据附加到集合中。运行时在内部使用锁对象上的就绪队列,因此它可以作为您的排队点


这允许您选择一个集合,例如具有预定义容量的
列表
,即用于追加的O(1)。您还可以加倍缓冲您的消费者,当您从“右”缓冲区合并时,回调将附加到“左”缓冲区,以此类推。这可以最大限度地减少生产者阻塞和相关丢失数据的数量,这对于突发数据非常方便。随着线程数量的变化,您还可以随时测量高水位线和处理率。

我不太关心可维护性。因为这是应用程序的任务关键部分,所以它的文档化程度很高,非程序员可以找到解决方法。谢谢你回答这个问题。还有,关于使用队列的好指针。我正在看新的
ConcurrentQueue
类,可能会走这条路。我在一个新项目中使用了Rx,它非常棒。这种情况下的问题是,它将需要对遗留代码进行一次大修,因为它甚至改变了处理问题的整个方式。对于下一个主要版本,我已经在进行这方面的工作,但对于一个小型(er)重新设计来说,这似乎是一项艰巨的任务。