使用大量信号量的不良 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",线程池会在创建新线程之前稍等片刻。
当我 运行 很多操作并行使用 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",线程池会在创建新线程之前稍等片刻。