我已经设置了一组Console.WriteLines,并且据我所知,当我在.net fiddle中运行以下命令时,它们都没有被调用。

using System;
using System.Net;
using System.Linq.Expressions;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Collections.Generic;

public class Program
{
    private static readonly object locker = new object();
    private static readonly string pageFormat = "http://www.letsrun.com/forum/forum.php?board=1&page={0}";

    public static void Main()
    {
        var client = new WebClient();

        // Queue up the requests we are going to make
        var tasks = new Queue<Task<string>>(
            Enumerable
            .Repeat(0,50)
            .Select(i => new Task<string>(() => client.DownloadString(string.Format(pageFormat,i))))
        );

        // Create set of 5 tasks which will be the at most 5
        // requests we wait on
        var runningTasks = new HashSet<Task<string>>();
        for(int i = 0; i < 5; ++i)
        {
            runningTasks.Add(tasks.Dequeue());
        }

        var timer = new System.Timers.Timer
        {
            AutoReset = true,
            Interval = 2000
        };

        // On each tick, go through the tasks that are supposed
        // to have started running and if they have completed
        // without error then store their result and run the
        // next queued task if there is one. When we run out of
        // any more tasks to run or wait for, stop the ticks.
        timer.Elapsed += delegate
        {
            lock(locker)
            {
                foreach(var task in runningTasks)
                {
                    if(task.IsCompleted)
                    {
                        if(!task.IsFaulted)
                        {
                            Console.WriteLine("Got a document: {0}",
                                task.Result.Substring(Math.Min(30, task.Result.Length)));

                            runningTasks.Remove(task);

                            if(tasks.Any())
                            {
                                runningTasks.Add(tasks.Dequeue());
                            }
                        }
                        else
                        {
                            Console.WriteLine("Uh-oh, task faulted, apparently");
                        }
                    }
                    else if(!task.Status.Equals(TaskStatus.Running)) // task not started
                    {
                        Console.WriteLine("About to start a task.");
                        task.Start();
                    }
                    else
                    {
                        Console.WriteLine("Apparently a task is running.");
                    }
                }

                if(!runningTasks.Any())
                {
                    timer.Stop();
                }
            }

        };
    }
}

我也很感激你给我的建议,告诉我如何简化或修正这个错误的逻辑。我尝试的模式是
(1)创建n个任务的队列
(2)创建一组m个任务,从(1)开始的第一个m个出列项
(3)启动M任务运行
(4)X秒后,检查已完成的任务。
(5)对于任何已完成的任务,对结果执行某些操作,从集合中移除该任务,并将其替换为队列中的另一个任务(如果有任务留在队列中)。
(6)无限期重复(4)-(5)。
(7)如果集合没有任务,我们就完成了。
但是也许有更好的方法来实现它,或者有一些.net函数可以很容易地封装我要做的事情(web请求与指定的最大并行度并行)。

最佳答案

代码中有几个问题,但由于您正在寻找更好的实现方法,因此可以使用Parallel.ForParallel.ForEach

Parallel.For(0, 50, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (i) =>
{
     // surround with try-catch
     string result;
     using (var client = new WebClient()) {
          result = client.DownloadString(string.Format(pageFormat, i));
     }
     // do something with result
     Console.WriteLine("Got a document: {0}", result.Substring(Math.Min(30, result.Length)));
});

它将并行执行主体(在任何给定时间不超过5个任务)。当一个任务完成时-下一个任务开始,直到它们全部完成,就像你想要的那样。
更新。使用这种方法可以有几个等待来限制任务,但最简单的方法是睡眠:
Parallel.For(0, 50, new ParallelOptions() { MaxDegreeOfParallelism = 5 },
(i) =>
{
    // surround with try-catch
    var watch = Stopwatch.StartNew();
    string result;
    using (var client = new WebClient()) {
         result = client.DownloadString(string.Format(pageFormat, i));
    }
    // do something with result
    Console.WriteLine("Got a document: {0}", result.Substring(Math.Min(30, result.Length)));
    watch.Stop();
    var sleep = 2000 - watch.ElapsedMilliseconds;
    if (sleep > 0)
          Thread.Sleep((int)sleep);
});

07-24 09:44
查看更多