C# 有没有办法在ASP.NET Web API应用程序中全局限制并行任务的数量?
我有一个ASP.NET 5 Web API应用程序,其中包含一个方法,该方法从C# 有没有办法在ASP.NET Web API应用程序中全局限制并行任务的数量?,c#,asp.net,async-await,task,C#,Asp.net,Async Await,Task,我有一个ASP.NET 5 Web API应用程序,其中包含一个方法,该方法从列表中获取对象,并向服务器发出HTTP请求,每次5个,直到所有请求完成。这是使用一个信号量lim、一个列表()、并等待任务完成的。WhenAll(),类似于下面的示例代码段: 公共异步任务DoStuff(列表输入数据) { const int maxDegreeOfParallelism=5; var tasks=新列表(); 使用var节流器=新信号量lim(maxDegreeOfParallelism); fore
列表中获取对象,并向服务器发出HTTP请求,每次5个,直到所有请求完成。这是使用一个信号量lim
、一个列表()
、并等待任务完成的。WhenAll()
,类似于下面的示例代码段:
公共异步任务DoStuff(列表输入数据)
{
const int maxDegreeOfParallelism=5;
var tasks=新列表();
使用var节流器=新信号量lim(maxDegreeOfParallelism);
foreach(inputData中的var输入)
{
添加(ExecHttpRequestAsync(输入,节流器));
}
List resposnes=wait Task.WhenAll(tasks).configurewait(false);
回应;
}
专用异步任务ExecHttpRequestAsync(输入,信号量限制)
{
wait throttler.WaitAsync().ConfigureWait(false);
尝试
{
使用var request=newhttprequestmessage(HttpMethod.Post)https://foo.bar/api");
request.Content=newstringcontent(JsonConvert.SerializeObject(input,Encoding.UTF8,“application/json”);
var response=await HttpClientWrapper.SendAsync(请求).ConfigureAwait(错误);
var responseBody=await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseObject=JsonConvert.DeserializeObject(responseBy);
返回响应对象;
}
最后
{
节流器释放();
}
}
这很好,但是我希望限制在整个应用程序中并行执行的任务总数,以允许此应用程序的扩展。例如,如果同时传入50个对我的API的请求,这将启动最多250个并行运行的任务。如果我想限制总数在任何给定时间执行的任务的数量,比如说100,是否有可能完成此任务?可能通过队列
?框架是否会自动防止执行太多任务?或者我是否以错误的方式处理此问题,是否需要将传入的请求排入我的应用程序队列
如果我想将在任何给定时间执行的任务总数限制在100个,那么有可能实现这一点吗
您想要的是限制所谓的的。您可以控制它管理的任务的最大一致性级别
。我建议实现一个类似队列的对象,跟踪传入请求和当前工作请求,并在消耗更多之前等待当前请求完成。下面的信息可能仍然相关
任务调度器负责如何确定任务的优先级,并负责跟踪任务,确保任务至少最终完成
它这样做的方式实际上与您提到的非常相似,通常任务调度器处理任务的方式是FIFO(先进先出)模型,与任务调度器的工作方式非常相似()
框架会自动防止执行过多的任务吗
默认情况下,使用大多数应用程序创建的TaskScheduler默认值为int.MaxValue
的MaximumConcurrencyLevel
。因此理论上是
事实上,任务数量实际上没有限制(至少在默认的TaskScheduler
)对于您的案例场景来说可能没什么大不了的
任务分为两种类型,至少在如何分配给可用线程池时是这样
在不太详细的情况下,其工作方式是如果任务创建其他任务,则这些新任务是父任务队列(本地队列)的一部分。父任务生成的任务仅限于父任务的线程池。()
如果一个任务不是由另一个任务创建的,那么它是一个顶级任务,并被放入全局队列中。这些任务通常会被分配自己的线程(如果可用),如果一个线程不可用,它将在FIFO模型中处理,如上所述,直到它的工作可以完成为止
这一点很重要,因为尽管您可以限制使用任务调度器发生的并发量,但这一点可能并不重要——例如,如果您有一个标记为长时间运行并负责处理传入请求的顶级任务。这将很有帮助,因为所有任务都是由该顶级任务生成的-级别任务将是该任务的本地队列的一部分,因此不会对线程池中的所有可用线程进行垃圾邮件处理。我假设代码是固定的,即任务。Run
被删除,并且WaitAsync
/释放被调整为限制HTTP调用,而不是列表。添加
我希望在整个应用程序中限制并行执行的任务总数,以允许扩展此应用程序
这对我来说没有意义。限制你的任务会限制你的扩展
例如,如果同时传入50个对我的API的请求,那么最多会启动250个并行运行的任务
同时,当然可以,但不能并行。需要注意的是,这些线程不是250个线程,它们也不是等待空闲线程池线程运行的250个CPU限制的操作。这些是承诺任务,不是委托任务,因此它们根本不在线程上“运行”。内存中只有250个对象
如果我想将在任何给定时间执行的任务总数限制在100个,那么有可能实现这一点吗
由于(这类)任务只是内存中的对象,因此不需要限制它们,就像您需要限制字符串
s或列表
s的数量一样。在确实需要的地方应用节流;例如。
private const int maxConcurrencyGlobal = 100;
private static SemaphoreSlim globalThrottler
= new SemaphoreSlim(maxConcurrencyGlobal, maxConcurrencyGlobal);
public async Task<ResponseObj[]> DoStuffAsync(IEnumerable<Input> inputData)
{
const int maxConcurrencyPerRequest = 5;
var perRequestThrottler
= new SemaphoreSlim(maxConcurrencyPerRequest, maxConcurrencyPerRequest);
Task<ResponseObj>[] tasks = inputData.Select(async input =>
{
await perRequestThrottler.WaitAsync();
try
{
await globalThrottler.WaitAsync();
try
{
return await ExecHttpRequestAsync(input);
}
finally { globalThrottler.Release(); }
}
finally { perRequestThrottler.Release(); }
}).ToArray();
return await Task.WhenAll(tasks);
}
private const int maxConcurrencyGlobal = 100;
private static SemaphoreSlim globalThrottler
= new SemaphoreSlim(maxConcurrencyGlobal, maxConcurrencyGlobal);
public async Task<ResponseObj[]> DoStuffAsync(IEnumerable<Input> inputData)
{
const int maxConcurrencyPerRequest = 5;
var perRequestThrottler
= new SemaphoreSlim(maxConcurrencyPerRequest, maxConcurrencyPerRequest);
var tasks = new List<Task<ResponseObj>>();
foreach (var input in inputData)
{
await perRequestThrottler.WaitAsync();
await globalThrottler.WaitAsync();
Task<ResponseObj> task = Run(async () =>
{
try
{
return await ExecHttpRequestAsync(input);
}
finally
{
try { globalThrottler.Release(); }
finally { perRequestThrottler.Release(); }
}
});
tasks.Add(task);
}
return await Task.WhenAll(tasks);
static async Task<T> Run<T>(Func<Task<T>> action) => await action();
}