了解SemaphoreSlim问题?

时间:2019-10-20 15:33:47

标签: 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++)
            {
                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();
            }
        }

如果我是正确的:在我的上下文中,我的信号量应只允许3个任务执行其工作,然后释放它们,然后让其他2个任务完成其工作。

问题

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

这是2个输出结果。

输出

enter image description here

2 个答案:

答案 0 :(得分:1)

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

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

您的所有任务都同时运行(可能有非常小的初始延迟),并且它们都具有相同的优先级(SemaphoreSlim不保证等待信号量的事物的顺序—这基本上是随机的,将为其分配等待的任务)。因此,有时候任务0、1和2在给task3提供信号之前就完成了,有时事情以不同的顺序发生也就不足为奇了。

如果您在每个任务尝试获取信号量,实际获取信号量,然后释放它的时候添加了一些额外的日志记录,则应该使其更加清晰-您将能够看到一个任务释放了该信号量,然后另一个任务立即将其拾取。)

答案 1 :(得分:0)

方法SemaphoreSlim.Wait

  

阻止当前线程,直到它可以输入SemaphoreSlim

方法SemaphoreSlim.WaitAsync

  

异步等待输入SemaphoreSlim

由于代码是异步的,因此应使用WaitAsync而不是Wait。通过在运行异步代码时阻塞线程,您将获得各种有趣的效果,而不是预期的行为。例如,考虑一下await Task.Delay(1000)之后,异步工作流可能会继续在其他线程上运行,或者可能不会继续运行,具体取决于您无法控制的条件。

长话短说,只需将mSemaphore.Wait()替换为await mSemaphore.WaitAsync()就可以了。