简体   繁体   English

与thread.sleep一起使用时如何在无限循环中执行多个任务

[英]How to do multiple tasks in an infinite loop while using with thread.sleep

I want to execute an .exe every morning at 4:00 AM. 我想每天早上4:00 AM执行一个.exe。 I did this using following code. 我使用以下代码进行了此操作。

  while (true)
  {
      var now = DateTime.Now;
      var schedule = new DateTime(now.Year, now.Month, now.Day, 4, 00, 00);
      if (schedule < now) schedule = schedule.AddDays(1);

      Thread.Sleep(schedule.Subtract(now));

      Process.Start("sample.exe");
  }

Now I want to execute one more tasks, like deleting a particular folder and subfolders every night at 1:00 AM. 现在,我想执行另一个任务,例如每天晚上1:00 AM删除特定的文件夹和子文件夹。 So for deleting I will use code like 因此,对于删除,我将使用类似

Directory.Delete(folder_path, recursive: true);

Should I use the same infinite loop or achieve it in some other way? 我应该使用相同的无限循环还是以其他方式实现它?

You shouldn't use Thread.Sleep for timing. 您不应将Thread.Sleep用于计时。 Try to avoid Thread.Sleep. 尝试避免Thread.Sleep。 You can only interrupt a sleep if you abort that thread. 如果中止该线程,则只能中断睡眠。 A better way would be a ManualResetEvent.Wait() which can be interrupted by setting it. 更好的方法是ManualResetEvent.Wait() ,可以通过设置它来中断它。


My advise is, use a timer: 我的建议是,使用计时器:

Create a timer that is checking a list of jobs. 创建用于检查作业列表的计时器。 Add the next time to execute on the job. 添加下一次在作业上执行。 A timer won't block the MainThread, so you can safely terminate the application. 计时器不会阻止MainThread,因此您可以安全地终止应用程序。 You could check the Jobs (interval) each minute or even higher. 您可以每分钟或更长时间检查一次作业(间隔)。

PSEUDO!! 伪装!!

class MyJob
{
    public DateTime ExecuteTime { get; set; }
    public Action Action { get; set; }
}

List<MyJob> Jobs = new List<MyJob>();

public void TimerCallback(object sender, EventArgs e)
{
    foreach (var job in Jobs)
    {
        if (job.ExecuteTime <= DateTime.Now)
        {
            try
            {
                job.Action();
            }
            catch(Exception exception)
            {
                // log the exception..
            }
            finally
            {
                job.ExecuteTime = job.ExecuteTime.Add(TimeSpan.FromDays(1));
            }
        }
    }
}

// this AddJob checks if the first execute should be today or tomorrow
public void AddJob(TimeSpan executeOn, Action action)
{
    var now = DateTime.Now;

    var firstExec = new DateTime(now.Year, now.Month, now.Day, 4, 0, 0);

    if (firstExec < now)  // time for today is already passed, next day..
        firstExec = firstExec.Add(TimeSpan.FromDays(1));

    Jobs.Add(new MyJob { ExecuteTime = firstExec, Action = action });
}

public void Add()
{
    // Add the jobs.
    AddJob(new TimeSpan(4, 0, 0), RunMyJob);
    AddJob(new TimeSpan(5, 0, 0), RunMyJob2);
}

public void RunMyJob()
{
    // delete or move, whatever...
}

public void RunMyJob2()
{
    // delete or move, whatever...
}

If all you're doing is scheduling a single .exe, you can use the Windows Task Scheduler - it's a lot easier than trying to do it yourself. 如果您要做的只是计划一个.exe,则可以使用Windows Task Scheduler-它比您自己尝试做要容易得多。 If you need several tasks done at several different times, you can create several .exes. 如果需要在几个不同的时间完成多个任务,则可以创建多个.exe。 (Sorry to be brief, I'm writing this on a phone). (很抱歉,我正在用手机写这篇文章)。

I would use Microsoft's Reactive Framework (NuGet "System.Reactive") for this. 我将为此使用Microsoft的Reactive Framework(NuGet“ System.Reactive”)。

You can then write this handy helper method: 然后,您可以编写以下方便的辅助方法:

public IDisposable ScheduleDaily(TimeSpan when, Action what)
{
    var now = DateTime.Now;
    var schedule = now.Date.Add(when);
    if (schedule < now)
    {
        schedule = schedule.AddDays(1.0);
    }

    return
        Observable
            .Timer(schedule, TimeSpan.FromDays(1.0))
            .Subscribe(_ => what());
}

To call it just do this: 要调用它,只需执行以下操作:

IDisposable runSample = ScheduleDaily(TimeSpan.FromHours(4.0),
    () => Process.Start("sample.exe"));

IDisposable deleteFolders = ScheduleDaily(TimeSpan.FromHours(1.0),
    () => Directory.Delete(folder_path, recursive: true));

The ScheduleDaily returns a handy IDisposable that you can .Dispose() to stop the timer. ScheduleDaily返回一个方便的IDisposable ,您可以使用.Dispose()停止计时器。

No threads or sleeping required. 无需线程或睡眠。 Very simple. 很简单。

Thread.Sleep() causes the current thread to sleep, so that thread won't be doing anything again until it's done sleeping (unless you interrupt it or something). Thread.Sleep()导致当前线程进入睡眠状态,因此该线程在进入睡眠状态之前不会再次执行任何操作(除非您中断它或进行其他操作)。

So, if you do it like that, you just need more threads. 因此,如果您这样做,则只需要更多线程即可。

var scheduleLoopAction = new Action<int, int, string>((int hour, int minute, string processName) =>
{
    while (true)
    {
        var now = DateTime.Now;
        var schedule = new DateTime(now.Year, now.Month, now.Day, hour, minute, 00);
        if (schedule < now) schedule = schedule.AddDays(1);

        System.Threading.Thread.Sleep(schedule.Subtract(now));

        System.Diagnostics.Process.Start(processName);
    }
});

((new System.Threading.Thread(() => { scheduleLoopAction(4, 0, "sample.exe"); }))).Start();
((new System.Threading.Thread(() => { scheduleLoopAction(1, 0, "sample2.exe"); }))).Start();

This probably isn't the best way accomplish the goal, but I figured that you'd wanted something like what was in the question. 这可能不是实现目标的最佳方法,但我认为您想要的是问题中的内容。

Note that the thread calls don't block, so this method will finish right away (unlike the sample in the question, which'll go forever). 请注意,线程调用不会阻塞,因此该方法将立即完成(与问题中的示例不同,它将永远存在)。 If you want it to block, you can just have the last schedule run in the current thread, eg 如果要阻止它,则可以在当前线程中运行最后一个调度,例如

((new System.Threading.Thread(() => { scheduleLoopAction(4, 0, "sample.exe"); }))).Start();
scheduleLoopAction(1, 0, "sample2.exe");  // Runs in current thread

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM