使用 SemaphoreSlim 限制并行任务的数量 - 为什么它有效?

Limiting the number of parallel task with SemaphoreSlim - why does it work?

在 MS Docu 中,您可以阅读有关 SemaphoreSlim 的信息: „代表信号量的轻量级替代方案,它限制可以同时访问资源或资源池的线程数。“
https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim?view=net-5.0

在我的理解中,任务不同于线程。任务比线程更高级别。不同的任务可以 运行 在同一个线程上。或者一个任务可以在另一个线程上继续而不是在它开始的时候。
(比较:“.NET 中使用异步的服务器端应用程序将使用非常少的线程,而不会限制它们自己。如果一切真的可以由单个线程提供服务,那很可能是——如果你永远只有一件事要做就物理处理而言,那很好。” 来自 in C# how to run method async in the same thread)

IMO 如果你把这些信息放在一起,结论是你不能限制任务的数量 运行ning 与使用信号量 slim 并行,但是......

有人可以提供一些信息来澄清吗?

   class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            IRunner runner = new RunnerSemaphore();
            runner.Run();

            Console.WriteLine("Hit any key to close...");
            Console.ReadLine();
        }
    }
    public class RunnerSemaphore : IRunner
    {
        private readonly SemaphoreSlim _ConcurrencySemaphore;
        private List<int> _Numbers;
        private int _MaxDegreeOfParallelism = 3;
        private object _RunningTasksLock = new object();
        private int _RunningTasksCount = 0;

        public RunnerSemaphore()
        {
            _ConcurrencySemaphore = new SemaphoreSlim(_MaxDegreeOfParallelism);
            _Numbers = _Numbers = Enumerable.Range(1, 100).ToList();
        }

        public void Run()
        {
            RunAsync().Wait();
        }        

        private async Task RunAsync()
        {
            List<Task> allTasks = new List<Task>();            

            foreach (int number in _Numbers)
            {
                var task = Task.Run
                    (async () =>
                    {
                        await _ConcurrencySemaphore.WaitAsync();

                        bool isFast = number != 1; 
                        int delay = isFast ? 200 : 10000;

                        Console.WriteLine($"Start Work {number}\tManagedThreadId {Thread.CurrentThread.ManagedThreadId}\tRunning {IncreaseTaskCount()} tasks");
                        await Task.Delay(delay).ConfigureAwait(false);
                        Console.WriteLine($"End Work {number}\tManagedThreadId {Thread.CurrentThread.ManagedThreadId}\tRunning {DecreaseTaskCount()} tasks");
                    })
                    .ContinueWith((t) =>
                    {
                        _ConcurrencySemaphore.Release();
                    });


                allTasks.Add(task);
            }

            await Task.WhenAll(allTasks.ToArray());
        }

        private int IncreaseTaskCount()
        {
            int taskCount;
            lock (_RunningTasksLock)
            {                
                taskCount = ++ _RunningTasksCount;
            }
            return taskCount;
        }

        private int DecreaseTaskCount()
        {
            int taskCount;
            lock (_RunningTasksLock)
            {
                taskCount = -- _RunningTasksCount;
                 
            }
            return taskCount;
        }        
    }

Represents a lightweight alternative to Semaphore that limits the number of threads that can access a resource or pool of resources concurrently.

嗯,当 SemaphoreSlim 首次推出时,这是一个非常好的描述 - 它只是一个轻量级的 Semaphore。从那时起,它获得了新方法(即 WaitAsync),使其能够像 异步 同步原语一样工作。

In my understanding a Task is different from Thread. Task is higher level than Thread. Different tasks can run on the same thread. Or a task can be continued on another thread than it was started on.

对于我所说的“委派任务”而言,情况确实如此。在其他语言中也有一个 completely different kind of Task that I call "Promise Tasks". Promise tasks are similar to promises (or "futures")(例如 JavaScript),它们只是表示某个事件的完成。承诺任务不会在任何地方“运行”;他们只是根据某些未来事件(通常通过回调)完成。

async 方法总是 return 承诺任务。异步方法中的 code 实际上并不是 运行 作为任务的一部分;任务本身只代表完成 async 方法。我推荐我的 async intro 以获取有关 async 以及如何安排代码部分的更多信息。

if you put this information together, the conclusion is that you can’t limit the number of Tasks running in parallel with the use of a semaphore slim

这是个人喜好,但我尽量对术语非常小心,正是为了避免像这个问题这样的问题。委托任务可以 运行 并行,例如 Parallel。 Promise 任务不会“运行”,它们不会运行“并行”,但是你可以有多个 concurrent promise 任务都是 进行中SemaphoreSlimWaitAsync 是限制这种并发的完美匹配。

您可能希望阅读有关 Stephen Toub's AsyncSemaphore 的文章(以及该系列中的其他文章)。它与 SemaphoreSlim 的实现不同,但就承诺任务而言,其行为基本相同。