C# 循环内的任务

C# 循环内的任务,c#,task,C#,Task,我有一个windows服务,线程每2分钟运行一次 while (true) { try { repNeg.willExecuteLoopWithTasks(param1, param2, param3); Thread.Sleep(20000); } 在这个循环中,我有一个任务循环: foreach (RepModel repModelo in listaRep) { Task t = new Task(() => { th

我有一个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分钟,我的线程就会得到一个要执行的任务列表,然后开始一个循环

但有时我的任务还没有完成,另一个线程又重新启动,然后奇怪的事情出现在我的日志中。 我试图用措辞来解决我的问题,但在执行一段时间后,有时需要几天,我的日志显示:


“System.IndexAutoFrangeException”

如果您使用的是framework
4.0
及更多版本,您可以尝试从中受益

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

并行代码可能如下所示:

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

});

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

下面是我要做的

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

  • 一个
    RepModel
    ID(独一无二的东西)
  • 上次运行的
    DateTime
  • a
    int
    表示任务应在几秒钟内运行的频率
  • 用于确定任务是否正在进行的
    bool
然后你需要一个类的全局列表,比如说“工作列表”

你的主应用程序应该有一个计时器,每隔几分钟运行一次。此计时器的任务是检查新的
RepModel
(假设这些参数会随时间而变化,即数据库列表)。勾选时,is循环列表并将任何新的(不同ID)添加到
作业列表
。您可能还希望删除不再需要的任何内容(即从DB列表中删除)

然后你有第二个计时器,它每秒钟运行一次。它的任务是检查
作业列表中的所有项目,并将上次运行时间与当前时间进行比较(确保它们尚未进行)。如果持续时间已超过1圈,则开始执行任务。任务完成后,更新上一次运行时间,以便下次可以运行,确保在运行过程中更改“进行中”标志

这都是理论,你需要自己尝试一下,但我认为它涵盖了你真正想要实现的目标


一些示例代码(可能编译/工作,也可能不编译/工作):

课堂作业
{
公共int ID{get;set;}
公共日期时间?上次运行{get;set;}
公共整数频率{get;set;}
公共布尔进程{get;set;}
}
列表作业列表=新列表();
//每2分钟(或任何时间)。
void timerMain_Tick()
{
foreach(listaRep中的RepModel repModelo)
{
如果(!JobList.Any)(x=>x.ID==repModelo.ID)
{
添加(新作业(){ID=repModel.ID,频率=120});
}
}
}
//每10秒(或任何时间)。
void timerTask_Tick()
{
foreach(作业列表中的var作业,其中(x=>!x.InProgress&&(x.LastRun==null | | | DateTime.Compare(x.LastRun.AddSeconds(x.Duration),DateTime.Now)<0))
{
任务t=新任务(()=>{
//完成任务。
}).ContinueWith(任务=>{
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();
    }
}
公共类Foo
{
私有ConcurrentDictionary令牌查找=
新的ConcurrentDictionary();
公共异步任务启动(RepModel)
{
var cts=新的CancellationTokenSource();
tokenLookup[model]=cts;
而(!cts.IsCancellationRequested)
{
等待任务。运行(()=>model.DoWork());
等待任务延迟(时间跨度从分钟(1));
}
}
公共无效端(RepModel模型)
{
取消源cts;
if(tokenLookup.TryRemove(model,out-cts))
cts.Cancel();
}
}

您不需要task,只需调用该函数即可。我认为您应该考虑使用Timer类。您的实现不好。我会研究异步方法。您不想阻止线程。@Derek如果执行预期的工作,它是被阻止的线程吗?旁注:
thread.Sleep(20000)
将睡眠20秒,而不是2分钟。使用线程。睡眠在我看来不是一种好的做法,你所做的是从线程池中取出一个线程,让它在2分钟内不做任何事情。这是一种糟糕的做法,因为它可能会回到线程池中,用作其他内容的资源。@DStanleyTigran,我不能等待所有任务完成。请编辑我Yquestion@PauloJardim:那么,如果一个任务尚未完成,而另一个任务需要启动,该怎么办?Tigran,应该只跳过这一个。但这不是并行引擎本身的问题,但是,这一点上,问题在于任务Scehduler,他向队列中注入了一个尚未完成的作业…任何给定的作业都可以是唯一的吗
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();
    }
}