2

我有一个包含 100 个网址的列表。我需要获取这些 url 的 html 内容。可以说我不使用异步版本,DownloadString而是执行以下操作。

var task1 = SyTask.Factory.StartNew(() => new WebClient().DownloadString("url1"));

我想要实现的是一次最多获取 4 个 url 的 html 字符串。

我为前四个 url 启动了 4 个任务。假设第 2 个 url 完成,我想立即为第 5 个 url 启动第 5 个任务。等等。这种方式最多只能下载 4 个 url,并且出于所有目的,总是会下载 4 个 url,即直到处理完所有 100 个。

我似乎无法想象我将如何实际实现这一目标。必须有一个既定的模式来做到这一点。想法?

编辑:

跟进@Damien_The_Unbeliever 的评论使用Parallel.ForEach,我写了以下

var urls = new List<string>();
var results = new Dictionary<string, string>();
var lockObj = new object();
Parallel.ForEach(urls,
                 new ParallelOptions { MaxDegreeOfParallelism = 4 },
                 url =>
                 {
                     var str = new WebClient().DownloadString(url);
                     lock (lockObj)
                     {
                         results[url] = str;
                     }
                 });

我认为上面的内容比创建单个任务和使用信号量来限制并发性更好。也就是说,我从未使用过或使用过Parallel.ForEach,我不确定这是否正确地完成了我需要做的事情。

4

2 回答 2

8
SemaphoreSlim sem = new SemaphoreSlim(4);
foreach (var url in urls)
{
    sem.Wait();
    Task.Factory.StartNew(() => new WebClient().DownloadString(url))
         .ContinueWith(t => sem.Release());    
}
于 2013-09-02T06:52:24.007 回答
1

实际上,Task.WaitAny对于您要实现的目标而言,这比ContinueWith

int tasksPerformedCount = 0

Task[] tasks = //initial 4 tasks

while(tasksPerformedCount< 100)
{
    //returns the index of the first task to complete, as soon as it completes
    int index = Task.WaitAny(tasks); 
    tasksPerformedCount++;

    //replace it with a new one
    tasks[index] = //new task            
}

编辑Task.WaitAny:来自http://www.amazon.co.uk/Exam-Ref-70-483-Programming-In/dp/0735676828/ref=sr_1_1?ie=UTF8&qid=1378105711&sr=8-1&keywords=exam+的另一个例子参考+70-483+编程+in+c

namespace Chapter1 {    
  public static class Program     {         
    public static void Main()         { 
      Task<int>[] tasks = new Task<int>[3]; 
        tasks[0] = Task.Run(() => { Thread.Sleep(2000); return 1; });
        tasks[1] = Task.Run(() => { Thread.Sleep(1000); return 2; });
        tasks[2] = Task.Run(() => { Thread.Sleep(3000); return 3; });

        while (tasks.Length > 0)
        {
          int i = Task.WaitAny(tasks);
          Task<int> completedTask = tasks[i];

          Console.WriteLine(completedTask.Result);

          var temp = tasks.ToList();
          temp.RemoveAt(i);
          tasks = temp.ToArray();
        }
      }
    }
  }
于 2013-09-02T06:59:32.333 回答