在asp.net中限制异步任务,并限制N个成功任务

本文关键字:任务 成功 异步 asp net | 更新日期: 2023-09-27 18:13:48

我使用的是Asp.Net 4.5.1。我有一些任务要运行,这些任务调用web服务,有些可能会失败。我需要运行N个成功的任务,这些任务执行一些较轻的CPU工作,主要调用web服务,然后停止,我想节流。

例如,假设某个集合中有300个URL。我们需要在它们中的每一个上运行一个名为Task<bool> CheckUrlAsync(url)的函数,并进行节流,这意味着,例如,只有5个"同时"运行(换句话说,在任何给定时间最多使用5个连接(。此外,我们只想执行N次(假设100次(成功操作,然后停止。

我读过这个和这个,但我仍然不确定什么是正确的方法。

你会怎么做?

  1. 假设ASP.Net
  2. 假设IO调用(对web服务器的http调用(,没有繁重的CPU操作

在asp.net中限制异步任务,并限制N个成功任务

使用Semaphore slim。

var semaphore = new SemaphoreSlim(5);
var tasks = urlCollection.Select(async url => 
{
    await semaphore.WaitAsync();
    try
    {
        return await CheckUrlAsync(url);
    } 
    finally 
    {
        semaphore.Release();
    }
};
while(tasks.Where(t => t.Completed).Count() < 100)
{
    await.Task.WhenAny(tasks);
}

尽管我更喜欢使用Rx.Net来生成一些更好的代码。

using(var semaphore = new SemaphoreSlim(5))
{
    var results = urlCollection.ToObservable()
              .Select(async url => 
    {
        await semaphore.WaitAsync();
        try
        {
            return await CheckUrlAsync(url);
        } 
        finally 
        {
            semaphore.Release();
        }
    }).Take(100).ToList();
}

好吧。。。这会很有趣。

public static class SemaphoreHelper
{
    public static Task<T> ContinueWith<T>(
         this SemaphoreSlim semaphore, 
         Func<Task<T>> action)
    var ret = semaphore.WaitAsync()
                   .ContinueWith(action);
    ret.ContinueWith(_ => semaphore.Release(), TaskContinuationOptions.None);
    return ret;
}
var semaphore = new SemaphoreSlim(5);
var results = urlCollection.Select(
              url => semaphore.ContinueWith(() => CheckUrlAsync(url)).ToList();

我需要补充的是,目前的代码仍将运行所有300个URL,只是会更快地返回。。。仅此而已。您需要将取消令牌添加到semaphore.WaitAsync(token)以取消排队的工作。我再次建议使用Rx.Net。使用Rx.Net可以更容易地获得用于.Take(100)的取消令牌。

试试这样的东西?

private const int u_limit = 100;
private const int c_limit = 5;
List<Task> tasks = new List<Task>();
int totalRun = 0;
while (totalRun < u_limit)
{
    for (int i = 0; i < c_limit; i++)
    {
      tasks.Add(Task.Run (() => {
      // Your code here.
      }));
    }
  Task.WaitAll(tasks);
  totalRun += c_limit;
}