内循环任务

时间:2014-12-16 13:51:16

标签: c# task

我有一个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"

3 个答案:

答案 0 :(得分:2)

这就是我要做的......

创建一个存储以下内容的新类(作为属性):

  • RepModel ID(独特的内容)
  • 最后一次DateTime
  • int表示任务应在几秒钟内运行的频率
  • a 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等,您可以尝试从

中受益

Parallel.ForEach

  

执行foreach操作,其中迭代可以并行运行。

并行代码可能如下所示:

Parallel.ForEach(listaRep , repModelo => {
     this.coletaFunc(repModelo.EndIp, user, tipoFilial);

});

这将在多个核心上运行(如果可能的话),并且您不需要一些特定的任务程序,因为您的代码将等到并行循环内的所有并行任务完成。如果满足条件,则可以递归调用相同的函数。

相关问题