.net System.Threading.Tasks-限制并发任务的数量
我刚刚开始研究.NET4.0中新的“System.Threading.Tasks”,并想知道是否有任何内置支持来限制一次运行的并发任务的数量,或者是否应该手动处理 例如:如果我需要调用一个计算方法100次,有没有一种方法可以设置100个任务,但同时只执行5个任务?答案可能是创建5个任务,调用Task.WaitAny,然后在前一个任务完成后创建一个新任务。我只是想确保如果有更好的方法,我不会错过一个技巧 基本上,是否有一种内置的方法可以做到这一点:.net System.Threading.Tasks-限制并发任务的数量,.net,asp.net,multithreading,task,.net,Asp.net,Multithreading,Task,我刚刚开始研究.NET4.0中新的“System.Threading.Tasks”,并想知道是否有任何内置支持来限制一次运行的并发任务的数量,或者是否应该手动处理 例如:如果我需要调用一个计算方法100次,有没有一种方法可以设置100个任务,但同时只执行5个任务?答案可能是创建5个任务,调用Task.WaitAny,然后在前一个任务完成后创建一个新任务。我只是想确保如果有更好的方法,我不会错过一个技巧 基本上,是否有一种内置的方法可以做到这一点: Dim taskArray() = {New T
Dim taskArray() = {New Task(Function() DoComputation1()),
New Task(Function() DoComputation2()),
...
New Task(Function() DoComputation100())}
Dim maxConcurrentThreads As Integer = 5
RunAllTasks(taskArray, maxConcurrentThreads)
感谢您的帮助。看起来不是这样,尽管您可以创建一个子类
TaskScheduler
来实现这种行为。简短回答:如果您想要的是限制辅助任务的数量,使它们不会使您的web服务饱和,那么我认为您的方法很好
详细回答:在.NET 4.0中,新的System.Threading.Tasks引擎运行在.NET线程池之上。因为每个进程只有一个线程池,默认情况下最多有250个工作线程。因此,如果将线程池的最大线程数设置为一个更适中的数目,则可以减少并发执行的线程数,从而减少使用ThreadPool.SetMaxThreads(…)
API执行的任务
但是,请注意,您可能不是唯一一个使用ThreadPool的人,因为您使用的许多其他类也可能将项目排队到ThreadPool。因此,这样做很有可能会破坏应用程序的其余部分。还要注意的是,由于线程池使用一种算法来优化其对给定机器底层内核的使用,因此将线程池可以排队的线程数限制在任意低的数目可能会导致一些灾难性的性能问题
同样,如果您想执行少量辅助任务/线程来执行某些任务,那么只创建少量任务(与100个任务相比)是最好的方法。我知道这是一个旧线程,但我只想分享我对这个问题的解决方案:使用信号量 (这是C#)
private void RunAllActions(IEnumerable actions,int-maxConcurrency)
{
使用(SemaphoreSlim concurrency semaphore=new SemaphoreSlim(maxConcurrency))
{
foreach(动作中的动作)
{
Task.Factory.StartNew(()=>
{
concurrency-ysemaphore.Wait();
尝试
{
动作();
}
最后
{
并发Maphore.Release();
}
});
}
}
}
一个解决方案可能是查看Microsoft预先制作的代码
描述如下:“提供了一个任务调度程序,确保在线程池上运行时达到最大并发级别。”,就我所能测试的而言,它似乎做到了这一点,就像ParallelOptions中的MaxDegreeOfParallelism属性一样。我知道这已经快一年了,但我找到了一个更容易实现这一点的方法,因此我想与大家分享:
Dim actionsArray() As Action =
new Action(){
New Action(Sub() DoComputation1()),
New Action(Sub() DoComputation2()),
...
New Action(Sub() DoComputation100())
}
System.Threading.Tasks.Parallel.Invoke(New Tasks.ParallelOptions() With {.MaxDegreeOfParallelism = 5}, actionsArray)
瞧 如果您的程序使用webservices,同时连接的数量将限制为
ServicePointManager.DefaultConnectionLimit
属性。如果你想要5个同时连接,使用Arrow_Raider的解决方案是不够的。您还应该增加ServicePointManager.DefaultConnectionLimit
,因为默认情况下它仅为2。C#相当于James提供的示例
Action[] actionsArray = new Action[] {
new Action(() => DoComputation1()),
new Action(() => DoComputation2()),
//...
new Action(() => DoComputation100())
};
System.Threading.Tasks.Parallel.Invoke(new Tasks.ParallelOptions {MaxDegreeOfParallelism = 5 }, actionsArray)
演示如何使用任务和操作执行此操作,并提供一个示例项目,您可以下载并运行该项目来查看这两个操作
用行动
如果使用操作,则可以使用内置的.Net Parallel.Invoke函数。这里我们限制它最多并行运行5个线程
var listOfActions = new List<Action>();
for (int i = 0; i < 100; i++)
{
// Note that we create the Action here, but do not start it.
listOfActions.Add(() => DoSomething());
}
var options = new ParallelOptions {MaxDegreeOfParallelism = 5};
Parallel.Invoke(options, listOfActions.ToArray());
var listOfActions=new List();
对于(int i=0;i<100;i++)
{
//注意,我们在这里创建操作,但不启动它。
添加(()=>DoSomething());
}
var options=new ParallelOptions{maxdegreeofpparallelism=5};
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'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'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);
}
}
//
///启动给定的任务并等待它们完成。这将最多并行运行指定数量的任务。
///注意:如果给定任务之一已启动,将引发异常。
///
///要运行的任务。
///并行运行的最大任务数。
///取消令牌。
public static void StartAndWaitAllThrottled(IEnumerable tasksToRun,int maxstaskstorunipallel,CancellationToken CancellationToken=new CancellationToken())
{
StartAndWaitAllThrottled(tasksToRun,MaxTaskStorUninPallel,-1,cancellationToken);
}
///
///启动给定的任务并等待它们完成。这将最多并行运行指定数量的任务。
///注意:如果给定任务之一已启动,将引发异常。
///
///要运行的任务。
///并行运行的最大任务数。
///在允许另一个任务启动之前,我们应该允许max任务并行运行的最长毫秒数。指定-1无限期等待。
///取消令牌。
public static void StartAndWaitAllThrottled(IEnumerable tasksToRun,int-maxTasksToRunInParallel,int-timeoutin毫秒,CancellationToken CancellationToken=new 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="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'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'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);
}
}
var listOfTasks = new List<Task>();
for (int i = 0; i < 100; i++)
{
var count = i;
// Note that we create the Task here, but do not start it.
listOfTasks.Add(new Task(() => Something()));
}
Tasks.StartAndWaitAllThrottled(listOfTasks, 5);