C# 如何检查此间隔是否正在运行?
我已经设置了一组C# 如何检查此间隔是否正在运行?,c#,.net,multithreading,asynchronous,parallel-processing,C#,.net,Multithreading,Asynchronous,Parallel Processing,我已经设置了一组控制台.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.Generi
控制台.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();
}
}
};
}
}
使用系统;
Net系统;
使用System.Linq.Expressions;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
使用系统计时器;
使用System.Collections.Generic;
公共课程
{
私有静态只读对象锁定器=新对象();
专用静态只读字符串pageFormat=”http://www.letsrun.com/forum/forum.php?board=1&page={0}";
公共静态void Main()
{
var client=new WebClient();
//将我们要提出的请求排队
var tasks=新队列(
可枚举
.重复(0,50)
.Select(i=>newtask(()=>client.DownloadString(string.Format(pageFormat,i)))
);
//创建一组5个任务,最多5个
//我们等待的请求
var runningTasks=new HashSet();
对于(int i=0;i<5;++i)
{
runningTasks.Add(tasks.Dequeue());
}
var timer=新的System.Timers.timer
{
自动重置=真,
间隔=2000
};
//在每个勾号上,完成假设的任务
//是否已开始运行,以及是否已完成
//然后无错误地存储结果并运行
//下一个排队的任务(如果有)。当我们用完
//要运行或等待的任何其他任务,请停止计时。
timer.appead+=委托
{
锁(储物柜)
{
foreach(runningTasks中的var任务)
{
如果(任务已完成)
{
如果(!task.IsFaulted)
{
WriteLine(“获取了一个文档:{0}”,
task.Result.Substring(Math.Min(30,task.Result.Length));
运行任务。删除(任务);
if(tasks.Any())
{
runningTasks.Add(tasks.Dequeue());
}
}
其他的
{
WriteLine(“哦,显然是任务出错”);
}
}
否则,如果(!task.Status.Equals(TaskStatus.Running))//任务未启动
{
WriteLine(“即将开始任务”);
task.Start();
}
其他的
{
WriteLine(“显然有任务正在运行”);
}
}
如果(!runningTasks.Any())
{
timer.Stop();
}
}
};
}
}
我也希望得到关于如何简化或修复这方面任何错误逻辑的建议。我想做的模式是
(1) 创建一个包含N个任务的队列
(2) 创建一组M个任务,第一个M个从(1)中退出队列的项目
(3) 启动正在运行的M任务
(4) X秒后,检查是否已完成任务
(5) 对于任何已完成的任务,对结果执行一些操作,从集合中删除该任务,并将其替换为队列中的另一个任务(如果队列U中还有任何任务)
(6) 无限期地重复(4)-(5)
(7) 如果集合中没有剩余任务,我们就完成了
但是也许有更好的方法来实现它,或者可能有一些.NET函数可以轻松地封装我正在尝试做的事情(web请求以指定的最大并行度并行)。代码中有几个问题,但是,由于您正在寻找更好的实现方法,您可以使用
Parallel.for
或Parallel.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);
});
这不是对你问题的直接回答。我只是想提出另一种方法 我建议您考虑使用Microsoft的反应式框架(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()
随时停止进程 您选择这种方法而不只是将任务推送到线程池中有什么原因吗?我不是说你错了,我只是好奇。一个带有特定DOP的简单ActionBlock可以解决这个问题。我不想这么做的一个原因是,它有可能让网站充斥着大量的请求。如果我很快得到前5个响应,我仍然希望在发出下一批请求之前等待一两秒钟。您知道站点是如何限制并发请求的吗?我的意思是,像“Y秒内不超过X个请求”之类的东西。不,我没有。但是让我们假设我这样做了:那么对于X和Y来说,这个过程是什么呢?你真的不应该做new-WebClient()。DownloadString(
asWebClient
是IDisposable
@Enigmativity),而一般来说,WebClient的Dispose没有任何用处(它是IDisposable的,因为它继承自IComponent),因此不处理它不会有什么坏处(不会留下任何打开的流、挂起的web请求或类似的内容)。在这种情况下添加如何限制请求不会有什么坏处(例如,作者需要在每批5个请求之间至少间隔两秒)@Evk Rx使用Rx进行节流要容易得多,它已经有操作员来节流或缓冲events@PanagiotisKanavos是的,但是