【发布时间】:2020-01-29 11:00:22
【问题描述】:
当我为每个操作使用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 之前添加一个 Debug.WriteLine 并观察结果
-
顺便说一句,您确实没有看到 SemaphoreSlim 的性能,而是看到 TaskScheduler 的工作方式(添加更多的 WorkerThread);o)
-
您正在看到 Thread.Sleep() 如何使用处于活动状态但未取得进展的线程来破坏线程池。考虑
await Task.Delay(1000);。除非 Sleep() 应该模拟正在完成的实际工作,代码燃烧约 90 亿条指令,否则这正是你想要避免让机器着火的原因。 -
@HansPassant 成功了,谢谢!但我认为 50 号对于线程数来说很小。您能否详细解释一下这种行为?你可以写成答案
-
50 仅在您拥有一台具有 50 个处理器内核的机器时很小。可以拥有,但不便宜。你的机器有 4 个,这是相当标准的。所以实际上它是在模拟 4*9 = 360 亿条指令的执行。您要求它执行 50*9 = 4500 亿条指令。这不可避免地需要超过一秒钟的时间。请记住,这是一项巨大的工作,Sleep(1000) 是准确模拟的可能性非常小。
标签: c# .net multithreading asynchronous semaphore