如何检查此间隔是否正在运行?

时间:2016-11-24 08:36:50

标签: c# .net multithreading asynchronous parallel-processing

我已经设置了一堆Console.WriteLine s,据我所知,当我在.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请求并行指定的最大程度并行)。

2 个答案:

答案 0 :(得分:3)

您的代码中存在多个问题,但由于您正在寻找更好的方法来实现它 - 您可以使用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);
});

答案 1 :(得分:2)

这不是您问题的直接答案。我只想提出一种替代方法。

我建议您考虑使用Microsoft的Reactive Framework(NuGet“System.Reactive”)来执行此类操作。

然后你可以这样做:

var query =
    Observable
        .Range(0, 50)
        .Select(i => string.Format(pageFormat, i))
        .Select(u => Observable.Using(
            () => new WebClient(),
            wc => Observable.Start(() => new { url = u, content = wc.DownloadString(u) })))
        .Merge(5);

IDisposable subscription = query.Subscribe(x =>
{
    Console.WriteLine(x.url);
    Console.WriteLine(x.content);
});

所有异步都可以通过调用subscription.Dispose()来随时停止该过程;