一次启动多个任务,然后在完成任务时启动另一个任务

时间:2014-10-10 11:15:01

标签: c# asp.net asp.net-web-api

我有一个代码在asp.net web api中运行,从其他站点下载图像文件。

这是我用来下载图片的代码。

       foreach (string file in images)
        {
            using (WebClient client = new WebClient())
            {
               await client.DownloadFileTaskAsync(file, path + Path.GetFileName(file));                    
            }
        }

我真正需要做的是一次启动10次下载。 (例如,使用Task.Run ......或任何其他方式),然后在最初启动的10逐个完成时逐个启动其余部分。

对于EX。

启动10个任务

等到10个任务中的一个完成

当1完成时,启动另一个任务

当另一个1完成时,启动另一个任务等......

直到下载列表中的所有图像为止。

因此,当代码运行时,我最多只能有10个并发下载(如果没有更多图像需要下载,则会少于10个。)

我正在使用.net 4.5.2

你们能指出我正确的方向来实现这个目标吗?

2 个答案:

答案 0 :(得分:2)

您可以使用SemaphoreSlim来限制异步工作:

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10);

...

var downloads = images.Select(file => DownloadAsync(file));
await Task.WhenAll(downloads);

...

private async Task DownloadAsync(string file)
{
  await _mutex.WaitAsync();
  try
  {
    using (WebClient client = new WebClient())
    {
      await client.DownloadFileTaskAsync(file, path + Path.GetFileName(file));                    
    }
  }
  finally
  {
    _mutex.Release();
  }
}

注意:

  • 您可能需要调整System.Net.ServicePointManager.DefaultConnectionLimit
  • 您应该避免在ASP.NET上使用Task.Run。有关详细信息,请参阅我的recent MSDN article on async ASP.NET

答案 1 :(得分:0)

我在这里大声思考,不确定这是否有效,其他人希望能有所改进:

private void startFirst10Tasks()
{
    List<Task> tasks = new List<Task>();
    //tasks.Add first 10 tasks
    taskHandler(tasks);
}

private void taskHandler(List<Task> tasks)
{
     Task.Factory.ContinueWhenAny(tasks, winner =>
        {
            getNewTasks(tasks.Except(new List<Task>{winner}));
        });
}

private void getNewTasks(List<Task> tasks)
{
    tasks.Add(
        Task.Factory.StartNew(() => 
        {
            //Next DownloadFileTask
        })
    );

    taskHandler(tasks);
}