【问题标题】:Timer to exact time by seconds计时器以秒为单位精确时间
【发布时间】:2017-03-16 20:49:04
【问题描述】:

我正在尝试与其他代码并行运行一个无限循环,该循环在 SECOND 达到特定时间后执行。 在下面的代码中,我试图触摸确切的午夜时间,即:00 hh: 00 mm: 00 sec

using System;  // for Date/Time
using System.Threading.Tasks;  // for Parallel

public class Program
{
public static void Main(string[] args)
{
Parallel.Invoke(
() =>
     {
           Console.WriteLine("Begin first task...");
      },  // close first Action

async () =>
      {
         Console.WriteLine("Begin second task...");
         var midNight = "00:00:00";
         while (true)
            {
              TimeSpan duration = DateTime.Parse(midNight).Subtract(DateTime.Now);
              Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds", 
              duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

              if(duration.Days >= 1)
                await Task.Delay(8640000);
                    else if(duration.Hours >= 1)
                           await Task.Delay(360000);
                               else if(duration.Minutes >= 1)
                                      await Task.Delay(60000);
                                         else 
                                            await Task.Delay(1000);

              if(duration == TimeSpan.Zero) { 
                  Console.WriteLine("It is time... midnight is {0}", DateTime.Now);
    }  // close second Action `the async task`
)  // end of Parallel.Invoke
}  // End of Main
}  // End of Program

我能够使await 语句正常工作以达到所需的点,但锁定条件if(duration == TimeSpan.Zero) 永远不会变成true

【问题讨论】:

  • 如果我没看错,如果还剩不到一分钟,你还耽误一秒钟(await Task.Delay(1000);)?哪怕只有半秒?那可能是你的问题。
  • 如果您为此使用计划任务会有所帮助吗? Quartz.NET 是一个可以帮助你的库......另见:stackoverflow.com/questions/4529019/…
  • 值得注意的是,除非您完全确定循环将每毫秒运行几次,否则您尝试执行的操作可能是不可能的,而外部因素(例如垃圾收集)可能会干扰。你可能会更好地为已经达到或超过的时间设置一个标志。
  • 这里不相关但可能令人讨厌的错误:您使用 async lambda 作为 Parllel.Invoke 的参数,它不提供异步支持。第二个参数 (Func<Task>) 只会阻塞,直到您点击 await 语句。如果您的第一个 Action 恰好在那时完成,Parallel.Invoke 将完成并且程序将立即终止,而无需等待由 async 委托创建的 Task 完成。
  • 没有确切的时间。这是宇宙量子性质的结果。你到底想做什么?您实际上需要多接近?

标签: c# task-parallel-library infinite-loop .net-core timespan


【解决方案1】:

我可以通过改变条件来做到这一点:

if(duration == TimeSpan.Zero)

到:

if(duration.Days == 0 && duration.Hours == 0 && duration.Minutes == 0 && duration.Seconds == 0)

为了避免事件的重复发生,我添加了另一个await级别,所以新的变成了:

if(duration.Days >= 1)
   await Task.Delay(8640000);
      else if(duration.Hours >= 1)
         await Task.Delay(360000);
            else if(duration.Minutes >= 1)
                await Task.Delay(60000);
                   else if(duration.Seconds >= 1)
                      await Task.Delay(1000);
                          else
                              await Task.Delay(500);

我尝试添加if(duration.Milliseconds >= 1) await(1),但没有成功,我的输出如下所示:

【讨论】:

  • 您可能希望在整个链条中执行await Task.Delay(TimeSpan.FromDays(1));await Task.Delay(TimeSpan.FromHours(1)); 等等,以使其更易于阅读。
  • 从技术角度来看,您的方法是错误的,因为如果在午夜冻结 2 秒,您的应用程序最终不会说“是时候了”。
  • @Logman,看起来真的很卡,但是为什么会这样,还是不明白。
  • @HasanAYousef 因为您可能使用的是普通计算机而不是 RTC 系统,所以您不能依赖确切的时间。您的应用程序无法运行可能有多种原因,但检查现在是否是午夜的整个想法是错误的。解决方案之一是检查您是否越过午夜,然后执行某些操作并重置计时器。另外Task.Delay 不保证它会等待 N 毫秒,只是它会等待至少 N 毫秒。
  • #Logman 能否请您发布一个建议的解决方案,以便我对其进行测试。谢谢
【解决方案2】:

问题在于,在您的情况下,duration 永远不会为零,它从正数毫秒变为负数毫秒。

我会做什么:如果duration 不到一秒,请等待duration,然后假设它是正确的时间。在代码中:

if (duration.Days >= 1)
    await Task.Delay(8640000);
else if (duration.Hours >= 1)
    await Task.Delay(360000);
else if (duration.Minutes >= 1)
    await Task.Delay(60000);
else if (duration.Seconds >= 1)
    await Task.Delay(1000);
else
{
    // defensive check in case duration is already negative
    // this should not normally happen, but is still possible
    if (duration > TimeSpan.Zero)
        await Task.Delay(duration);
    Console.WriteLine("It is time... midnight is {0}", DateTime.Now);
}

这不会为您提供毫秒精度,但在正常情况下应该是正确的秒数。

【讨论】:

  • 你的方法看起来很合乎逻辑,我会在周日检查并在周一确认。谢谢
【解决方案3】:

首先,您的程序无法像您展示的那样运行,因为您的TimeSpan duration = DateTime.Parse(midNight).Subtract(DateTime.Now); 永远不会是积极的。其次,您的主要功能几乎会立即返回。无论如何,您可以尝试我的解决方案来解决您的问题:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{

    public static void Main(string[] args)
    {
        Parallel.Invoke(
            () =>{Console.WriteLine("Begin first task...");},
            async () =>
            {
                Console.WriteLine("Begin second task...");
                DateTime startDate = DateTime.Today;
                while (true)
                {
                    TimeSpan duration = DateTime.Now.Subtract(startDate);
                    Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds", 
                                      duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

                    int delay = (int)(DateTime.Today.AddDays(1.0).Subtract(DateTime.Now).TotalMilliseconds/2);
                    await Task.Delay(delay>0?delay:0);

                    if(duration.Days >= 1) 
                    { 

                        Console.WriteLine("It is time... midnight is {0}", DateTime.Now);

                        startDate = DateTime.Today;
                    }

                }
            }
        );
    }
}

这不会为您提供超精确度,因为在普通 PC 上您无法获得它(您可以使用 IRQ 来获得更高的准确性,但这样的解决方案要复杂得多)。

也是概念教授link to 10s version

当时版本:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{

    public static void Main(string[] args)
    {
        Parallel.Invoke(
            async () =>
            {
                Console.WriteLine("Begin second task...");
                TimeSpan eventTime = new TimeSpan(0,18,16,53,123); //set when run event (ex. 18:16:53.123)
                DateTime endDate = DateTime.Today.Add(eventTime);
                if(endDate<DateTime.Now) endDate = endDate.AddDays(1.0);
                while (true)
                {
                    TimeSpan duration = endDate.Subtract(DateTime.Now);
                    Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds", duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

                    if(duration.TotalMilliseconds <= 0.0)
                    { 
                        Console.WriteLine("It is time... {0}", DateTime.Now);

                        endDate = endDate.AddDays(1.0);
                        continue;
                    }

                    int delay = (int)(duration.TotalMilliseconds/2);
                    await Task.Delay(delay>0?delay:0);

                }
            }
        );
        Thread.Sleep(6000);
    }
}

【讨论】:

  • 如果我想将它与给定时间进行比较,而不是午夜?
  • @HasanAYousef 我有一个小错误(我混合了 10s 版本和午夜版本),但现在可以了。现在回答你的问题。您将需要计算 endDate 而不是 startDate。然后在持续时间跨 0 之后,您将需要重新计算它并执行您的操作。我将在一分钟内发布示例。但是对于未来,请在您的问题中发布您的要求,不要稍后添加它们,因为它们可以完全改变实施。
  • @HasanAYousef 我添加了新版本。
  • 非常感谢,现在已经很完美了,我可以达到每毫秒的精度:) 以后的帖子会考虑你的注释,不胜感激。
  • @HasanAYousef 不客气。准确性将在很大程度上取决于您运行应用程序的环境。如果您在后台执行此操作并且用户正在玩视频游戏,那么您的应用程序执行事件之前甚至可能需要几秒钟。如果您对我的回答感到满意,也请投票。
猜你喜欢
  • 2019-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多