【问题标题】:Repeated Tasks using Timer Interval vs Task Delay使用计时器间隔与任务延迟的重复任务
【发布时间】:2017-10-16 05:58:15
【问题描述】:

我正在实施一项计划的工作,例如方法,并已缩小到方法。一个实现定时器间隔,另一个基于任务延迟。

我也考虑过使用 Azure 计时器触发的网络作业,但它们不能在多实例模式下工作。实际上,在多实例应用程序中,只有一个触发器在其中一个实例中触发,而其他触发器被锁定,因此增加我的应用程序的实例计数不会增加触发的事件数量。

方法 A(定时器间隔)

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

public class Example
{
    public static void Main()
    {
        var aTimer = new Timer();
        aTimer.Interval = 5000;
        aTimer.Elapsed += OnTimedEventA;
        aTimer.AutoReset = true;
        aTimer.Enabled = true;

        var bTimer = new System.Timers.Timer();
        bTimer.Interval = 2000;
        bTimer.Elapsed += OnTimedEventB;
        bTimer.AutoReset = true;
        bTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }
    private static void OnTimedEventA(Object source, System.Timers.ElapsedEventArgs e)
    {
        Task.Run(() =>
        {
            Console.WriteLine("The Elapsed event A was raised at {0}", DateTime.Now);
        });
    }
    private static void OnTimedEventB(Object source, System.Timers.ElapsedEventArgs e)
    {
        Task.Run(() =>
        {
            Console.WriteLine("The Elapsed event B was raised at {0}", DateTime.Now);
        });
    }
}

方法 B(任务延迟)

using System;
using System.Threading.Tasks;
public class Example
{
    public static void Main()
    {
        EventAAsync();
        EventBAsync();

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }
    private static async Task EventAAsync()
    {
        while (true)
        {
            Task.Run(() =>
            {
                Console.WriteLine("The Elapsed event A was raised at {0}", DateTime.Now);
            });
            await Task.Delay(TimeSpan.FromSeconds(5));
        }
    }
    private static async Task EventBAsync()
    {
        while (true)
        {
            Task.Run(() =>
            {
                Console.WriteLine("The Elapsed event B was raised at {0}", DateTime.Now);
            });
            await Task.Delay(TimeSpan.FromSeconds(2));
        }
    }
}

方法 A 和方法 B 在功能上都是一样的。 他们都以 5s 和 2s 的固定间隔调用火灾事件 A 和 B,然后忘记了。现在我试图了解这两种方法的优缺点。最后,事件本身在不同的线程上运行。在理论上和实践中,应该采用这两者中的哪一个来处理许多这样的事件触发器,以便在多个实例中进行缩放。

也欢迎任何其他方法。

【问题讨论】:

  • Task.Delay 在内部使用相同的Timer,所以选择你更喜欢的。
  • @Evk 除了使用延迟之外,它就像一次性计时器,而不是定期运行的计时器。一击计时器有问题并导致时钟随时间漂移。因此,长时间运行后,2 秒和 5 秒的间隔将不同步。
  • 对于方法 B,一般来说,我建议您:(1) await Task.WhenAll(workTask, delayTask) 而不是等待 just 延迟,以避免重入,以及 (2)添加取消令牌并使用while (!cancellationToken.IsCancellationRequested)

标签: c#


【解决方案1】:

方法 A 用于必须定期触发某些任务时。 方法 B 用于必须在触发某些任务之间提供定期间隔。

如果事件处理程序(触发后执行的任务)需要更长的时间来处理,则会有所不同。

方法A: 无论事件处理程序中的任务花费多少时间,事件处理程序都会在设定的时间间隔内不断被触发(代码必须是可重入的)。

方法B: 任务完成然后我们睡觉。所以如果任务需要不同的时间来完成,那么下一次触发也是在不同的时间

【讨论】:

  • 谢谢。但是在方法 B 中,我已经使用 Task.Run 运行了与事件相关的实际代码并忘记了时尚,所以这是否会使两者都执行相同的基于间隔的调用?
  • @ManikantaReddyD Task.Run 或 Task.Delay 只是将要在线程池中的线程上运行的工作项排队。考虑一下线程池中的线程用完的情况(可能是您的事件代码需要很长时间才能完成)。您的工作项开始排队。
  • 为强调可重入性而欢呼,这是计时器方法的一个重要属性。
  • 我同意 OP 的观点,即方法 B 实际上不受工作运行时间的影响(即不漂移)。他们在一个周期性的时间间隔内正确地实现了它。
【解决方案2】:

改用计时器,虽然两者都使用@Evk 指出的相同时钟,但第一种方法会永久运行计时器。

使用 Task.Delay 将同样准确,但在完成的延迟和开始新的延迟之间存在短暂的差距。很长一段时间后,它们可能会变得不同步。所以 5 到 2 秒的间隔与您开始计时的时间不同。

使用永久运行的Timer就没有这个问题了。

【讨论】:

  • 这可能是一种预期的行为
猜你喜欢
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 2018-10-21
相关资源
最近更新 更多