在用于 Semaphore 的 WaitOne(Int32) 方法的 MSDN docs 中,它说给它一个零值将导致该方法立即返回,而无需等待插槽打开。 SemaphoreSlim 版本的文档没有说明同样的事情。 SemaphoreSlim 是否也有这种行为?

我不确定如何保证自己测试的时间。

最佳答案

如果超时为 0,那么 SemaphoreSlim.Wait 将始终尝试在返回之前获取一个插槽,它只是不会等待一个比几个操作和一个 SpinOnce 更长的时间。

编辑:澄清: 这可能与 Semaphore 的明显行为相同。来自 Semaphore 的文档:



但是 SemaphoreSlim 确实通过使用 SpinOnce 为您在 Wait 方法中提供了打开插槽的机会。

(结束编辑)

此外,SemaphoreSlim 在尝试获取插槽之前运行一些操作。其中之一是 Monitor.Enter ,因此它可以在此时等待正在等待或释放过程中的其他线程。因此,它不一定会立即返回。

据我所知,事件的顺序是:

  • 创建一个 CancellationTokenRegistration
  • 如果可用槽数为 0 ,则 SpinWait.SpinOnce (如果 NextSpinWillYieldtrue 则跳过此)
    (编辑:我大胆强调第 2 步执行与第 5 步相同的测试,让它有机会旋转并在最终测试和退出之前有一个可用的插槽)
  • 调用 Monitor.Enter 进入锁(Release 和 WaitAsync 进入的同一个锁)
  • 将内部 waitCount 增加 1
  • 如果可用槽数为 0 ,将 waitCount 减 1 并退出锁,然后 返回 false
  • 如果我们到达这里并且有一个空闲插槽 :
  • 将可用槽数减 1(含义:获取槽)
  • 重置 AvailableWaitHandle 下的 waitHandle,如果它正在被使用并且可用插槽计数现在再次为 0
  • 将 waitCount 减 1
  • 退出锁
  • 处理 CancellationTokenRegistration
  • 返回 true

  • (请注意,其他使用非零超时的线程会通过调用 Monitor.Wait 间歇性地释放和获取用于保护计数器的锁,因此您不会在超时为 0 的情况下永远等待,只是很短的时间。)

    因此 SemaphoreSlim 似乎不会与 Sempahore 共享完全相同的 0 超时行为,因为它确实为插槽提供了打开的机会。 (也许这就是为什么有 Semaphore.WaitOneSemaphoreSlim.Wait - 通过更改信号量的实例化来升级旧代码时导致代码无法编译,从而让我们检查行为)。

    文章 Semaphore vs. SemaphoreSlim 没有强调这种行为,只是强调两者之间的根本区别。

    边注:

    有趣的是,该引用文献还指出



    然而 SemaphoreSlim.AvailableWaitHandle 的文档却有不同的说法。

    关于c# - SemaphoreSlim 的 Wait(Int32) 方法在传递为零时是否立即返回?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14574181/

    10-11 01:51