这是我的代码,但我不明白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
}
}
等待ss.WaitAsync(); & ss.Release();
做什么?
我想如果我一次运行50个线程然后编写像SemaphoreSlim ss = new SemaphoreSlim(10);
这样的代码,那么它将被强制运行10个活动线程。
当10个线程中的一个完成后,另一个线程将启动....如果我不对,那么请帮助我理解样本情况。
为什么await需要与ss.WaitAsync();
一起使用? ss.WaitAsync();
做了什么?
答案 0 :(得分:48)
我想如果我一次运行50个线程,那么代码就像SemaphoreSlim ss = new SemaphoreSlim(10);将强制在时间运行10个活动线程
这是正确的;信号量的使用确保了不会有超过10名工人同时从事这项工作。
在信号量上调用WaitAsync
会产生一个任务,当该线程被赋予对该令牌的“访问权限”时,该任务将完成。 await
- 该任务允许程序在“允许”的情况下继续执行。拥有异步版本,而不是调用Wait
,对于确保方法保持异步而不是同步,以及处理async
方法可以执行代码的事实非常重要几个线程,由于回调,因此与信号量的自然线程亲和力可能是一个问题。
旁注:DoPollingThenWorkAsync
不应该有Async
后缀,因为它实际上不是异步的,而是同步的。只需将其称为DoPollingThenWork
即可。它将减少读者的困惑。
答案 1 :(得分:2)
虽然我接受这个问题确实与倒计时锁定方案有关,但我认为值得分享我为那些希望将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中,以确保它始终被释放。
答案 2 :(得分:1)
在拐角处的幼儿园,他们使用SemaphoreSlim控制可在体育室玩多少个孩子。
他们在房间外面的地板上画了5对脚印。
孩子们到达时,他们把鞋子放在一双免费的脚印上,进入房间。
玩完游戏后,他们就会出来,收拾鞋子,为另一个孩子“释放”一个插槽。
如果孩子到了并且没有脚印了,他们会去别处玩或者呆一会儿,然后不时检查一下(即,没有FIFO优先级)。
当老师在附近时,她在走廊的另一侧“释放”额外的5个脚印行,以使另外5个孩子可以同时在房间里玩耍。
它也具有SemaphoreSlim的“陷阱” ...
如果一个孩子完成游戏并且离开房间而没有拿起鞋子(不会触发“释放”),则即使理论上有一个空的插槽,插槽也会被阻塞。通常,孩子通常会被告知。
有时候,一个或两个偷偷摸摸的孩子将鞋子藏在别处,然后进入房间,即使所有的脚印都已被占用(即SemaphoreSlim并不能“真正”控制房间里有多少个孩子)。
这通常不会很好地结束,因为房间过度拥挤往往会因孩子哭泣和老师完全关闭房间而结束。