需要了解SemaphoreSlim的用法

Mou*_*Mou 62 c# multithreading semaphore task-parallel-library

这是我的代码,但我不明白SemaphoreSlim在做什么.

async Task WorkerMainAsync()
{
  SemaphoreSlim ss = new SemaphoreSlim(10);
  List<Task> trackedTasks = new List<Task>();
  while (DoMore())
  {
    await ss.WaitAsync();
    trackedTasks.Add(Task.Run(() =>
              {
                DoPollingThenWorkAsync();
                ss.Release();
              }));
  }
  await Task.WhenAll(trackedTasks);
}

void DoPollingThenWorkAsync()
{
  var msg = Poll();
  if (msg != null)
  {
    Thread.Sleep(2000); // process the long running CPU-bound job
  }
}
Run Code Online (Sandbox Code Playgroud)

等待ss.WaitAsync(); & ss.Release();做什么?

我想如果我一次运行50个线程然后编写代码SemaphoreSlim ss = new SemaphoreSlim(10);那么它将被迫在时间运行10个活动线程.

当10个线程中的一个完成然后另一个线程将启动....如果我不对,那么请帮助我理解样本情况.

为什么需要等待使用ss.WaitAsync();?怎么ss.WaitAsync();办?

小智 119

在拐角处的幼儿园,他们使用 SemaphoreSlim 来控制有多少孩子可以在体育室玩耍。

他们在房间外面的地板上画了5对脚印。

当孩子们到达时,他们将鞋子留在一对免费的脚印上,然后进入房间。

一旦他们玩完了,他们就会出来,收集他们的鞋子并为另一个孩子“释放”一个插槽。

如果一个孩子到达并且没有留下脚印,他们会去其他地方玩耍或者只是在附近逗留一段时间并时不时地检查一下(即,没有 FIFO 优先级)。

当老师在身边时,她会在走廊的另一边“释放”一排额外的5个脚印,这样可以让5个孩子同时在房间里玩耍。

它也有与 SemaphoreSlim 相同的“陷阱”......

如果孩子玩完游戏并没有收集鞋子就离开了房间(不会触发“释放”),那么即使理论上有一个空插槽,插槽也会被阻塞。不过,孩子通常会被告知。

有时一两个鬼鬼祟祟的孩子把鞋子藏在别处进入房间,即使所有的脚印都已经被记录下来(即,SemaphoreSlim 并不能“真正”控制房间里有多少孩子)。

这通常不会有好的结果,因为房间过度拥挤往往会导致孩子们哭闹,而老师会完全关闭房间。

  • 这些类型的答案是我最喜欢的。 (21认同)
  • 如果您要锁定的是非托管资源,则“SemaphoreSlim”将不会获得操作系统级锁定(因此称为“slim”)。它只能封锁它直接控制的入口。 (4认同)
  • 请解释一下“SemaphoreSlim”如何实现这一点?“SemaphoreSlim 并不能“真正”控制房间里有多少孩子” (3认同)
  • 爱它!来自上帝的老师 (2认同)

Ser*_*rvy 55

我想如果我一次运行50个线程,那么代码就像SemaphoreSlim ss = new SemaphoreSlim(10); 将强制运行10个活动线程

那是正确的; 信号量的使用确保了不会有超过10名工人同时从事这项工作.

调用WaitAsync信号量会产生一个任务,当该线程被赋予对该令牌的"访问权"时,该任务将完成. await - 该任务允许程序在"允许"时继续执行.拥有异步版本而不是调用Wait,对于确保方法保持异步而不是同步,以及处理async由于回调等方法可以跨多个线程执行代码这一事实非常重要,因此与信号量的自然线程亲和力可能是一个问题.

旁注:DoPollingThenWorkAsync不应该有Async后缀,因为它实际上不是异步的,它是同步的.打电话吧DoPollingThenWork.它将减少读者的困惑.

  • @Thomas如果你有太多的并发线程,那么线程花费的时间超过了他们花在做生产性工作上的时间.随着线程的增加,吞吐量下降,因为您花费越来越多的时间来管理线程而不是工作,至少,一旦您的线程计数远远超过计算机上的核心数量. (4认同)
  • @Servy这是任务调度程序工作的一部分.任务!=线程.原始代码中的`Thread.Sleep`将破坏任务调度程序.如果你没有与核心异步,那你就不是异步. (3认同)

and*_*ate 8

虽然我接受这个问题确实与倒计时锁场景有关,但我认为值得分享我为那些希望使用 SemaphoreSlim 作为简单异步锁的人发现的链接。它允许您使用可以使编码更整洁、更安全的 using 语句。

http://www.tomdupont.net/2016/03/how-to-release-semaphore-with-using.html

我确实在它的 Dispose 中进行了交换_isDisposed=true_semaphore.Release()以防它以某种方式被多次调用。

同样重要的是要注意 SemaphoreSlim 不是可重入锁,这意味着如果同一线程多次调用 WaitAsync,则信号量的计数每次都会递减。简而言之,SemaphoreSlim 不是线程感知的。

关于代码质量问题,最好将 Release 放在 try-finally 的 finally 中,以确保它始终被发布。

  • 发布仅链接的答案是不可取的,因为链接往往会随着时间的推移而消失,从而使答案变得毫无价值。如果可以,最好将关键点或关键代码块汇总到您的答案中。 (6认同)