我有一个Windows服务,其中一个线程每2分钟运行一次。
while (true)
{
try
{
repNeg.willExecuteLoopWithTasks(param1, param2, param3);
Thread.Sleep(20000);
}
在这里我有一个循环任务:
foreach (RepModel repModelo in listaRep)
{
Task t = new Task(() => { this.coletaFunc(repModelo.EndIp, user, tipoFilial); });
t.Start();
}
但我认为这种做法是错误的。我只需要为列表中的每个元素运行一个任务, 并且,当特定任务完成时,等待一分钟然后重新开始。
M8我需要说我有两种情况。
1 - 我无法等待所有任务完成。因为某些任务可能需要2个多小时才能完成,而另一个任务只需要27秒。
2 - 我的任务列表可以更改。这就是为什么我有一个线程。每2分钟我的线程获得要执行的任务列表,然后开始循环。
但有时候我的任务还没有完成,另一个线程再次启动,然后在我的日志中显示奇怪的事情。 我尝试使用Dictionry解决我的问题,但经过一段时间的执行,有时需要几天,我的日志显示:
" System.IndexOutOfRangeException"
答案 0 :(得分:2)
这就是我要做的......
创建一个存储以下内容的新类(作为属性):
RepModel
ID(独特的内容)DateTime
int
表示任务应在几秒钟内运行的频率bool
以确定任务是否正在进行然后你需要一个类的全局列表,比如叫做" JobList"。
你的主应用程序应该有一个Timer,每隔几分钟运行一次。此计时器的工作是检查新的RepModel
(假设这些可以随时间变化,即数据库列表)。当这个滴答时,循环列表并将任何新的(不同的ID)添加到JobList
。您可能还想删除任何不再需要的(即从DB列表中删除)。
然后你有第二个计时器,每秒运行一次。它的工作是检查JobList
中的所有项目,并将上次运行时间与当前时间进行比较(并确保它们尚未进行中)。如果持续时间已经完成,则启动任务。任务完成后,更新上次运行时间,以便下次运行,确保更改正在进行的操作"你去吧。
这是所有理论,您需要自己尝试一下,但我认为它涵盖了您实际想要实现的目标。
一些示例代码(可能编译/不编译):
class Job
{
public int ID { get; set; }
public DateTime? LastRun { get; set; }
public int Frequency { get; set; }
public bool InProgress { get; set; }
}
List<Job> JobList = new List<Job>();
// Every 2 minutes (or whatever).
void timerMain_Tick()
{
foreach (RepModel repModelo in listaRep)
{
if(!JobList.Any(x => x.ID == repModelo.ID)
{
JobList.Add(new Job(){ ID = repModel.ID, Frequency = 120 });
}
}
}
// Every 10 seconds (or whatever).
void timerTask_Tick()
{
foreach(var job in JobList.Where(x => !x.InProgress && (x.LastRun == null || DateTime.Compare(x.LastRun.AddSeconds(x.Duration), DateTime.Now) < 0))
{
Task t = new Task(() => {
// Do task.
}).ContinueWith(task => {
job.LastRun = DateTime.Now;
job.InProgress = false;
}, TaskScheduler.FromCurrentSynchronizationContext());;
job.InProgress = true;
t.Start();
}
}
答案 1 :(得分:0)
所以你真正需要的是一个有两个操作的类,它需要能够开始处理你的一个模型,并且它需要能够结束你的一个模型的处理。将它与列表分开将使这更容易。
当您开始处理模型时,您将要创建一个与CancellationTokenSource
关联的模型,以便您以后可以停止处理它。在您的情况下,处理它意味着有一个循环,而不是取消,运行一个操作,然后等待一段时间。结束操作就像取消令牌源一样简单。
public class Foo
{
private ConcurrentDictionary<RepModel, CancellationTokenSource> tokenLookup =
new ConcurrentDictionary<RepModel, CancellationTokenSource>();
public async Task Start(RepModel model)
{
var cts = new CancellationTokenSource();
tokenLookup[model] = cts;
while (!cts.IsCancellationRequested)
{
await Task.Run(() => model.DoWork());
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
public void End(RepModel model)
{
CancellationTokenSource cts;
if (tokenLookup.TryRemove(model, out cts))
cts.Cancel();
}
}
答案 2 :(得分:-1)
如果您使用的是框架4.0
等,您可以尝试从
执行foreach操作,其中迭代可以并行运行。
并行代码可能如下所示:
Parallel.ForEach(listaRep , repModelo => {
this.coletaFunc(repModelo.EndIp, user, tipoFilial);
});
这将在多个核心上运行(如果可能的话),并且您不需要一些特定的任务程序,因为您的代码将等到并行循环内的所有并行任务完成。如果满足条件,则可以递归调用相同的函数。