为啥 BCL 中没有 AutoResetEventSlim?

Posted

技术标签:

【中文标题】为啥 BCL 中没有 AutoResetEventSlim?【英文标题】:Why no AutoResetEventSlim in BCL?为什么 BCL 中没有 AutoResetEventSlim? 【发布时间】:2012-01-03 03:54:06 【问题描述】:

为什么 BCL 中没有 AutoResetEventSlim 类?

可以用ManualResetEventSlim模拟吗?

【问题讨论】:

AutoResetEventSlim 可以使用SemaphoreSlim 进行模拟。 【参考方案1】:

ManualResetEventManualResetEventSlim 都被设计为在调用后保持信号状态。这通常适用于与AutoResetEvent 截然不同的场景。

AutoResetEvent 使用后立即返回无信号状态,这通常用于一组不同的场景。来自 AutoResetEvents 文档:

通常,当线程需要独占访问资源时,您会使用此类。

ManualResetEvent(和Slim)通常用于以下场景:

此通信涉及一个线程必须在其他线程继续之前完成的任务。

由于AutoResetEvent 最常用于有多个线程共享资源的场景,因此等待时间通常不会非常短。但是,ManualResetEventSlim 仅在您事先知道等待时间很短的情况下才真正打算使用。如果您的等待时间不会很短,那么您应该改用ManualResetEvent。有关详细信息,请参阅difference between MRE and MRES 上的文档。

当您的等待时间较长时(这将是AutoResetEvent 的正常情况),“slim”版本实际上更糟,因为它恢复为使用等待句柄。

【讨论】:

我认为不同的典型用例根本没有说明预期的等待时间。事实上,我希望“多个线程共享同一资源”比“一个线程必须在其他线程可以继续之前完成”的等待更少。【参考方案2】:

我也被这个事实困扰。 但是,您似乎可以使用带有特殊配置的简单SemaphoreSlim 模拟AutoResetEvent(Slim)

SemaphoreSlim Lock = new SemaphoreSlim( 1, 1 );

在the constructor中,第一个参数定义了信号量的初始状态:1表示可以进入一个线程,0表示必须先释放信号量。所以new AutoResetEvent( true ) 转换为new SemaphoreSlim( 1, 1 )new AutoResetEvent( false ) 分别转换为new SemaphoreSlim( 0, 1 )

第二个参数定义了可以同时进入信号量的最大线程数。将其设置为 1 使其行为类似于 AutoResetEvent

SemaphoreSlim 的另一个好处是,在 4.5 中使用新的async/await 模式,该类收到了一个可以等待的.WaitAsync() method。因此,在这种情况下,无需再手动创建可等待的等待原语。

希望这会有所帮助。

【讨论】:

使用SemaphoreSlim 作为AutoResetEvent 的替代品时的一个复杂情况:尝试在已达到最大计数时释放将抛出SemaphoreFullException,这与在已设置事件时设置事件不同。 【参考方案3】:

引用微软的 Josh Phillips (original here):

至于 AutoResetEventSlim.. 是否有您需要它的场景? 说实话,创建一个 AutoResetEvent 非常困难,而且 用例非常有限,因此我们优先考虑其他方面的工作。 如果你真的需要一个,我很想听听,所以我们 可以考虑创建一个!

扩展评论:您可以只使用手动事件并确保在等待后始终将其重置。

如果等待和重置之间的微小延迟导致了问题,那么无论如何都可能存在根本问题。例如,如果有多个消费者并且您应该只让一个通过,那么可能某个事件不是适合该工作的工具,您应该考虑 SemaphoreSlim 甚至只是原始的 Monitor

【讨论】:

以上是关于为啥 BCL 中没有 AutoResetEventSlim?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 BCL 集合使用结构枚举器,而不是类?

AutoResetEvent和ManualResetEvent理解 z

C#中AutoResetEvent的waitOne()方法会影响到所有用户么?

多线程的AutoResetEvent

可等待的 AutoResetEvent

在 C# 中异步等待继续后的实时 AutoResetEvent