C# 理解信号量LIM问题?

C# 理解信号量LIM问题?,c#,async-await,task,semaphore,C#,Async Await,Task,Semaphore,我这里有一个简单的测试代码,使用SemaphoreSlim static SemaphoreSlim mSemaphore = new SemaphoreSlim(3); static async Task Main() { var tasks = new Task[5]; for (var i = 0; i < tasks.Length; i++) {

我这里有一个简单的测试代码,使用SemaphoreSlim

        static SemaphoreSlim mSemaphore = new SemaphoreSlim(3);

        static async Task Main()
        {
            var tasks = new Task[5];

            for (var i = 0; i < tasks.Length; i++)
            {
                var taskNo = i;
                tasks[i] = Task.Run(() => DoWork($"task{taskNo}"));
            }

            await Task.WhenAll(tasks);
        }

        static async Task DoWork(string taskName)
        {
            for (var i = 0; i < 3; i++)
            {
                mSemaphore.Wait();

                Console.WriteLine($"{taskName}: doing {i}.");   
                await Task.Delay(1000); 

                mSemaphore.Release();
            }
        }
静态信号量lim mSemaphore=新信号量lim(3);
静态异步任务Main()
{
var任务=新任务[5];
对于(var i=0;iDoWork($“Task{taskNo}”);
}
等待任务。何时(任务);
}
静态异步任务DoWork(字符串taskName)
{
对于(变量i=0;i<3;i++)
{
mSemaphore.Wait();
WriteLine($“{taskName}:doing{i}.”);
等待任务。延迟(1000);
mSemaphore.Release();
}
}
如果我是正确的:在我的上下文中,我的信号量应该只允许3个任务完成它们的工作,然后它应该释放它们,然后它应该让我的其他2个任务完成它们的工作

问题

我对此进行了测试,但不幸的是,有时我会得到不同/错误的结果

下面是两个输出结果

输出


mSemaphore.Wait()
mSemaphore.Release()
的调用在
for
循环中。循环每次迭代后,每个任务都会释放信号量,然后再次尝试获取信号量

有鉴于此,没有什么可以阻止Task0、1或2在循环结束时释放信号量,以及task3获取信号量。释放它的任务将返回其循环的开始,并再次坐在
mSemaphore.Wait()
上,等待另一个任务释放信号量

您的所有任务都在同一时间运行(除了非常小的初始延迟,可能),并且它们都具有相同的优先级(信号量LIM不能保证等待信号量的事物获取它的顺序——本质上是随机的,等待的任务将被赋予它)。因此,有时任务0、1和2在任务3得到信号量之前完成,有时事情以不同的顺序发生,这并不奇怪

如果您在每个任务尝试获取信号量、实际获取信号量、然后释放信号量的位置添加一些额外的日志记录,那么应该会更清楚一点——您将能够看到一个任务释放信号量,然后另一个任务立即拾取信号量)。

方法:

阻止当前线程,直到它可以输入
信号量lim

方法:

异步等待输入
信号量lim

因为代码是异步的,所以应该使用
WaitAsync
,而不是
Wait
。通过在运行异步代码时阻塞线程,您将获得各种有趣的效果,而不是预期的行为。例如,在<代码>等待任务>延迟(1000)< /代码>之后,异步工作流可能继续运行在不同的线程上,或者可能不会,这取决于您不控制的条件。
长话短说,只要将
mSemaphore.Wait()
替换为
Wait mSemaphore.WaitAsync()
,您就会没事了。

只是一个友好的提示。SemaphoreSlim有一个异步版本,因此您的
mSemaphore.Wait()
可以替换为
wait mSemaphore.WaitAsync()