任务内部循环
本文关键字:循环 内部 任务 | 更新日期: 2023-09-27 18:34:19
我有一个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的我需要说我这里有2种情况。
1 - 我等不及所有任务完成。因为有些任务可能需要 2 个多小时才能完成,而另一个任务可能只需要 27 秒。
2 - 我的任务列表可以更改。这就是为什么我得到了一个线程。每 2 分钟,我的线程获取要执行的任务列表,然后启动循环。
但有时我的任务还没有完成,另一个线程再次开始,然后我的日志中会出现奇怪的事情。我尝试使用字典来解决我的问题,但经过一段时间的执行,有时需要几天,我的日志显示:
"System.IndexOutOfRangeException">
这是我要做的...
创建一个存储以下内容(作为属性(的新类:
RepModel
ID(唯一内容(- 最后一次运行的
DateTime
- 任务运行频率的
int
(以秒为单位( - 用于确定任务是否正在进行
bool
然后你需要一个类的全局列表,比如说"JobList"。
您的主应用程序应该有一个计时器,每隔几分钟运行一次。此计时器的工作是检查新RepModel
(假设这些会随着时间的推移而变化,即数据库列表(。当此刻度时,将循环列表并将任何新 ID(不同的 ID(添加到JobList
。您可能还希望删除不再需要的任何内容(即从数据库列表中删除(。
然后你有第二个计时器,每秒运行一次。它的工作是检查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();
}
}
您真正需要的是一个具有两个操作的类,它需要能够开始处理您的一个模型,并且它需要能够结束对其中一个模型的处理。 将其与列表分开将使此操作更容易。
当您开始处理模型时,您需要创建一个与之关联的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();
}
}
如果您正在使用框架4.0
等,则可以尝试从中受益
Parallel.ForEach
执行 foreach 操作,其中迭代可以并行运行。
并行代码可能如下所示:
Parallel.ForEach(listaRep , repModelo => {
this.coletaFunc(repModelo.EndIp, user, tipoFilial);
});
这将在多个内核上运行(如果可能的话(,并且您不需要一些特定的任务调度器,因为您的代码将等到并行循环中的所有并行任务完成。之后,如果满足条件,您可以递归调用相同的函数。