同时控制并行任务数运行

Controlling the number of parallel tasks running simultaneously

我有一个 Windows 服务应用程序,它有许多不同的作业需要并行完成,并按三个不同的时间表生成。但是我想确保在任何时候都不会超过 N 个任务 运行.

这不是 this question, because that is about limiting the number of tasks that are started per second, not the number that are running concurrently. Also not a duplicate of this 的副本,因为我的任务在不同的时间表上,所以不能同时排队。

这是一些提供上下文的代码:

namespace WindowsService1
{
    public partial class Service1 : ServiceBase
    {
        List<Task> ServiceTasks;
        private List<Timer> ServiceTimers;
        private static int N = 10;

        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            List<double> Times = new List<double>(){1000d,2000d,5000d};
            for(int i = 0; i<3 ; i++)
            {
                 var t = new Timer(Times[i]);
                 t.AutoReset = true;
                 ServiceTimers.Add(t);
            }
            ServiceTimers[0].Elapsed += Timer1Elapsed_DoSomething;
            ServiceTimers[0].Start();
            ServiceTimers[1].Elapsed += Timer2Elapsed_DoSomething;
            ServiceTimers[1].Start();
            ServiceTimers[2].Elapsed += Timer3Elapsed_DoSomething;
            ServiceTimers[2].Start();
        }

         private void Timer1Elapsed_DoSomething(object sender, ElapsedEventArgs e)
         {
            ServiceTasks.Add(Task.Factory.StartNew(() => ServiceWork.DoTask1()));
         }

         private void Timer2Elapsed_DoSomething(object sender, ElapsedEventArgs e)
         {
            ServiceTasks.Add(Task.Factory.StartNew(() => ServiceWork.DoTask2()));
         }

         private void Timer3Elapsed_DoSomething(object sender, ElapsedEventArgs e)
         {
            ServiceTasks.Add(Task.Factory.StartNew(() => ServiceWork.DoTask3()));
         }  
    }
}

如何修改此代码以确保在任何给定时间只有 N 个任务 运行?

SemaphoreSlim 是一个 class,可以让您做到这一点。是N个同步锁"threads".

像这样:

SemaphoreSlim s = ...;
async Task F() {
 await s.WaitAsync();
 DoWork();
 s.Release();
}

如果您不介意每个线程等待 1MB 的堆栈使用量,您也可以使用同步 API。


这个:

 Task.Factory.StartNew(() => ServiceWork.DoTask3())

变为:

Task.Run(async () => {
 await s.WaitAsync();
 try {
  ServiceWork.DoTask3();
 } finally {
  s.Release();
 }
});

而且你可以启动无限数量的这些,它们会自动节流。

仍然需要任务列表,以便您可以通过等待所有任务完成并处理它们的错误来彻底关闭服务。