【问题标题】:Can I use Task.Delay as a timer?我可以使用 Task.Delay 作为计时器吗?
【发布时间】:2022-01-06 10:38:37
【问题描述】:

我想每秒执行一些代码。我现在使用的代码是:

Task.Run((Action)ExecuteSomething);

ExecuteSomething()定义如下:

 private void ExecuteSomething()
        {
            Task.Delay(1000).ContinueWith(
               t =>
               {
                   //Do something.

                   ExecuteSomething();
               });
        }

这个方法会阻塞线程吗?还是应该在 C# 中使用 Timer 类?而且好像Timer also dedicates a separate thread for execution (?)

【问题讨论】:

  • 关于“单独线程”,所有定时器之间只有一个线程共享。

标签: c# multithreading asynchronous timer task


【解决方案1】:

Task.Delay 内部使用Timer

使用Task.Delay,您可以使您的代码比使用Timer 更清晰。并且使用async-await不会阻塞当前线程(通常是UI)。

public async Task ExecuteEverySecond(Action execute)
{
    while(true)
    {
        execute();
        await Task.Delay(1000);
    }
}

来自源代码:Task.Delay

// on line 5893
// ... and create our timer and make sure that it stays rooted.
if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
{
    promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);
    promise.Timer.KeepRootedWhileScheduled();
}

// ...

【讨论】:

  • 性能方面,对使用 Task.Delay => 每次创建一个新计时器有影响吗?
  • @JoelHarkes,影响将取决于使用情况,通常在所有性能方面的问题中 ;)
【解决方案2】:

Microsoft 的响应式框架非常适合这一点。只需 NuGet “System.Reactive” 即可获取这些信息。然后你可以这样做:

IDisposable subscription =
    Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Subscribe(x => execute());

如果您想停止订阅,只需致电subscription.Dispose()。除此之外,响应式框架可以提供比任务或基本计时器更多的功能。

【讨论】:

  • 在我的情况下使用 Rx 是否比使用 Task.Delay 有任何特定的优势?只是想知道是否值得为此目的包含该软件包。还是投了赞成票。
  • @Sharun - Rx 的功能与 LINQ 一样多,而且还有一些。根据您的描述,您不需要更多,但我怀疑您实际上会从 Rx 提供的东西中受益。你能描述更多关于你正在尝试做的事情吗?
【解决方案3】:

你可以使用 Task.Delay 作为计时器吗?这取决于您是需要等待一个确切的时间间隔,还是只需要在其他工作完成后等待指定的时间间隔。

换句话说,我过去曾多次使用 Timer 作为服务的一部分,该服务为同步目的而获取或推送数据。问题是,如果您每 60 秒获取一次数据,但数据库速度很慢,并且您的检索需要超过 60 秒。现在您遇到了一个问题,因为您将要获取相同的数据两次。因此,您更新该过程,以便停止计时器直到它完成,然后再次启动计时器。这工作正常,但使用 Task.Delay 可以为您工作。

因此,如果您只需要在两次执行之间延迟一点时间,那么 Task.Delay 就可以完美运行。

【讨论】:

    【解决方案4】:
    static class Helper
    {
        public async static Task ExecuteInterval(Action execute, int millisecond, IWorker worker)
        {
            while (worker.Worked)
            {
                execute();
    
                await Task.Delay(millisecond);
            }
        }
    }
    
    
    interface IWorker
    {
        bool Worked { get; }
    }
    

    【讨论】:

    • 请注意,如果execute 的运行时间与延迟相比甚至有些显着,这会漂移,即不遵守周期性间隔。
    • @Timo 人们可以考虑在 .NET 6 中使用PeriodicTimer 来解决这个漂移问题。
    猜你喜欢
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    • 2022-01-01
    • 2016-12-15
    • 1970-01-01
    • 2020-10-28
    相关资源
    最近更新 更多