使用大量信号量的不良 SemaphoreSlim 性能

Bad SemaphoreSlim perfomance using a lot of semaphores

当我 运行 很多操作并行使用 SemaphoreSlim 时,它们的调用没有预期的那么快。

这是代码

  var sw = new Stopwatch();
  sw.Start();

  for (int i = 0; i < 50; i++) {
    int localI = i;
    Task.Run(async () => {
       var semaphore = new SemaphoreSlim(1, 1);
       await semaphore.WaitAsync();
       Thread.Sleep(1000);
       counter++;
       semaphore.Release();
       Debug.WriteLine($"{localI} - {sw.ElapsedMilliseconds}");
     });
  }

  Thread.Sleep(5000);

这是输出:

2 - 1015
0 - 1015
1 - 1015
3 - 2053
4 - 2053
5 - 2053
6 - 2120
7 - 3009
8 - 3064
9 - 3066
10 - 3068
11 - 3134
12 - 4011
13 - 4016
14 - 4070
15 - 4071
16 - 4073
17 - 4140

有人能解释一下为什么它们没有在大约 1 秒内被调用吗?

您看到的是有限的线程池注入率。它与 SemaphoreSlim 甚至 async 无关,因为发布的所有代码实际上都是同步的。

在您的机器上,三个线程能够立即 运行。线程池看到它还有其他工作要做(47 个其他项目已经排队)。所以它稍等片刻,然后注入另一个线程。下一组工作使用四个线程。线程池还是"behind",所以稍等一下再注入另一个线程等

上面描述的"wait for a bit"部分是限制线程池注入率。线程池必须稍等片刻,否则每当它有更多的工作时,它会立即创建一堆线程,然后在工作完成后将其丢弃。因此,为了提高效率并防止这种情况发生 "thread thrashing",线程池会在创建新线程之前稍等片刻。