【问题标题】:Can I limit the amount of System.Threading.Tasks.Task objects that run simultaneously?我可以限制同时运行的 System.Threading.Tasks.Task 对象的数量吗?
【发布时间】:2012-02-08 16:20:37
【问题描述】:

我正在使用 Task 类进行多线程计算。

例如:

   List<Task> taskList = new List<Task>();
   for(int i=0;i<10;i++){
        var task = new Task(() => Console.WriteLine("Hello from taskA."));
        taskList.Add(task);
        task.Start();
    }

有没有办法只做,比如说,最多运行 3 个任务,其余的等待?

【问题讨论】:

标签: c# .net multithreading


【解决方案1】:

我建议查看here 的帖子。

换句话说,您创建一个相关任务的集合,然后使用 ParallelOptions.MaxDegreeOfParallelism 来控制同时执行的任务数量。

【讨论】:

    【解决方案2】:

    你可以把这个当作通用的

    public TaskThrottle(int initialCount, int maxTasksToRunInParallel)
    {
        _semaphore = new SemaphoreSlim(initialCount, maxTasksToRunInParallel);
    }
    
    public void TaskThrottler<T>(IEnumerable<Task<T>> tasks, int timeoutInMilliseconds, CancellationToken cancellationToken = default(CancellationToken)) where T : class
    {
            // Get Tasks as List
            var taskList = tasks as IList<Task<T>> ?? tasks.ToList();
            var semaphoreTasks = new List<Task<int>>();
    
            // When the first task completed, flag as done/release
            taskList.ForEach(x =>
            {
                semaphoreTasks.Add(x.ContinueWith(y => _semaphore.Release(), cancellationToken));
            });
    
            semaphoreTasks.ForEach(async x =>
            {
                // It will not pass this until one free slot available or timeout occure  
                if(timeoutInMilliseconds > 0)
                    await _semaphore.WaitAsync(timeoutInMilliseconds, cancellationToken);
                else
                    await _semaphore.WaitAsync(cancellationToken);
    
                // Throws a OperationCanceledException if this token has had cancellation requested
                cancellationToken.ThrowIfCancellationRequested();
    
                // Start the task 
                x.Start();
            });
    
            Task.WaitAll(semaphoreTasks.ToArray(), cancellationToken);
    }
    

    【讨论】:

      【解决方案3】:

      My blog post 展示了如何使用任务和操作来执行此操作,并提供了一个示例项目,您可以下载并运行以查看两者的实际效果。

      有动作

      如果使用 Actions,您可以使用内置的 .Net Parallel.Invoke 函数。这里我们限制它最多并行运行 3 个线程。

      var listOfActions = new List<Action>();
      for (int i = 0; i < 10; i++)
      {
          // Note that we create the Action here, but do not start it.
          listOfActions.Add(() => DoSomething());
      }
      
      var options = new ParallelOptions {MaxDegreeOfParallelism = 3};
      Parallel.Invoke(options, listOfActions.ToArray());
      

      有任务

      由于您在这里使用的是任务,因此没有内置函数。不过,您可以使用我在博客上提供的那个。

          /// <summary>
          /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel.
          /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
          /// </summary>
          /// <param name="tasksToRun">The tasks to run.</param>
          /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
          /// <param name="cancellationToken">The cancellation token.</param>
          public static void StartAndWaitAllThrottled(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = new CancellationToken())
          {
              StartAndWaitAllThrottled(tasksToRun, maxTasksToRunInParallel, -1, cancellationToken);
          }
      
          /// <summary>
          /// Starts the given tasks and waits for them to complete. This will run, at most, the specified number of tasks in parallel.
          /// <para>NOTE: If one of the given tasks has already been started, an exception will be thrown.</para>
          /// </summary>
          /// <param name="tasksToRun">The tasks to run.</param>
          /// <param name="maxTasksToRunInParallel">The maximum number of tasks to run in parallel.</param>
          /// <param name="timeoutInMilliseconds">The maximum milliseconds we should allow the max tasks to run in parallel before allowing another task to start. Specify -1 to wait indefinitely.</param>
          /// <param name="cancellationToken">The cancellation token.</param>
          public static void StartAndWaitAllThrottled(IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = new CancellationToken())
          {
              // Convert to a list of tasks so that we don&#39;t enumerate over it multiple times needlessly.
              var tasks = tasksToRun.ToList();
      
              using (var throttler = new SemaphoreSlim(maxTasksToRunInParallel))
              {
                  var postTaskTasks = new List<Task>();
      
                  // Have each task notify the throttler when it completes so that it decrements the number of tasks currently running.
                  tasks.ForEach(t => postTaskTasks.Add(t.ContinueWith(tsk => throttler.Release())));
      
                  // Start running each task.
                  foreach (var task in tasks)
                  {
                      // Increment the number of tasks currently running and wait if too many are running.
                      throttler.Wait(timeoutInMilliseconds, cancellationToken);
      
                      cancellationToken.ThrowIfCancellationRequested();
                      task.Start();
                  }
      
                  // Wait for all of the provided tasks to complete.
                  // We wait on the list of "post" tasks instead of the original tasks, otherwise there is a potential race condition where the throttler&#39;s using block is exited before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object.
                  Task.WaitAll(postTaskTasks.ToArray(), cancellationToken);
              }
          }
      

      然后创建您的任务列表并调用函数让它们运行,一次最多同时运行 3 个,您可以这样做:

      var listOfTasks = new List<Task>();
      for (int i = 0; i < 10; i++)
      {
          var count = i;
          // Note that we create the Task here, but do not start it.
          listOfTasks.Add(new Task(() => Something()));
      }
      Tasks.StartAndWaitAllThrottled(listOfTasks, 3);
      

      【讨论】:

        【解决方案4】:

        【讨论】:

          猜你喜欢
          • 2015-06-25
          • 1970-01-01
          • 2011-02-11
          • 2018-07-23
          • 2023-03-26
          • 2019-06-03
          • 2011-06-15
          • 1970-01-01
          相关资源
          最近更新 更多