asp.net 以指定的时间间隔定期运行CQC方法

roqulrg3  于 2023-10-21  发布在  .NET
关注(0)|答案(3)|浏览(120)

我需要将一些数据从C# Web应用程序发布到服务。数据本身是在用户使用应用程序时收集的(一种使用统计数据)。我不想在每个用户的请求期间向服务发送数据,我宁愿在应用程序中收集数据,然后在一个单独的线程中发送单个请求中的所有数据,这并不服务于用户的请求(我的意思是用户不必等待服务处理请求)。为此,我需要一种JS的setInterval模拟-每X秒启动一次函数,以将所有收集的数据刷新到服务。
我发现Timer类提供了一些类似的(Elapsed事件)。然而,这允许只运行一次方法,但这不是一个大问题。它的主要困难在于它需要签名

void MethodName(object e, ElapsedEventArgs args)

虽然我想启动candic方法,这将调用web服务(输入参数并不重要):

async Task MethodName(object e, ElapsedEventArgs args)

有人能建议如何解决所描述的任务吗?任何提示赞赏。

ryevplcw

ryevplcw1#

async的等价物是一个while循环和Task.Delay(内部使用System.Threading.Timer):

public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken)
{
    while (true)
    {
        await FooAsync();
        await Task.Delay(interval, cancellationToken)
    }
}

传递一个CancellationToken很重要,这样你就可以在需要的时候停止这个操作(例如,当您关闭应用程序时)。
现在,虽然这与.Net一般相关,但在ASP.NET中,做任何类型的火灾和遗忘都是危险的。有几种解决方案(如HangFire),有些在Fire and Forget on ASP.NET by Stephen Cleary中有记录,有些在How to run Background Tasks in ASP.NET by Scott Hanselman中有记录

yftpprvb

yftpprvb2#

下面是一个以周期性方式调用异步方法的方法:

public static async Task PeriodicAsync(Func<Task> action, TimeSpan interval,
    CancellationToken cancellationToken = default)
{
    while (true)
    {
        Task delayTask = Task.Delay(interval, cancellationToken);
        await action();
        await delayTask;
    }
}

提供的action在每个interval被调用,然后等待创建的Task。等待的持续时间不影响间隔,除非它碰巧比这更长。在这种情况下,不重叠执行的原则优先,因此,该期间将延长,以匹配等待的持续时间。
在异常情况下,PeriodicAsync任务将失败完成,因此如果您希望它具有错误弹性,则应该在action中包含严格的错误处理。
用法示例:

Task statisticsUploader = PeriodicAsync(async () =>
{
    try
    {
        await UploadStatisticsAsync();
    }
    catch (Exception ex)
    {
        // Log the exception
    }
}, TimeSpan.FromMinutes(5));

**.NET 6更新:**现在可以通过使用新的PeriodicTimer类来实现几乎相同的功能,而不会在每个循环上产生Task.Delay分配的成本:

public static async Task PeriodicAsync(Func<Task> action, TimeSpan interval,
    CancellationToken cancellationToken = default)
{
    using PeriodicTimer timer = new(interval);
    while (true)
    {
        await action();
        await timer.WaitForNextTickAsync(cancellationToken);
    }
}

WaitForNextTickAsync方法返回一个ValueTask<bool>,这使得这个实现更加高效。不过,效率上的差异非常小。对于每5分钟运行一次的周期性操作,在每次迭代中分配一些轻量级对象应该几乎没有影响。
基于PeriodicTimer的实现的行为与基于Task.Delay的实现不同。如果一个动作的持续时间比interval长,两个实现都会在前一个动作完成后立即调用下一个动作,但是基于PeriodicTimer的实现的调度程序不会像基于Task.Delay的实现那样向前滑动。请参阅下面的大理石图,以直观地展示差异:

Clock          X---------X---------X---------X---------X---------X---------X--
Task.Delay:    +-----|   +---|     +------------|+---|     +------|  +--|
PeriodicTimer: +-----|   +---|     +------------|+---| +------|  +--|      +--

基于Task.Delay的实现的调度被永久地向前移动,因为action的第三次调用持续的时间比interval长。
正如您所看到的,PeriodicTimer并不保证action调用之间的最小间隔。
上面的PeriodicAsync实现是出于教育目的而故意保持简单的。像参数验证和ConfigureAwait这样的东西被省略了。

roejwanj

roejwanj3#

简单的方法是使用任务和一个简单的循环:

public async Task StartTimer(CancellationToken cancellationToken) =>
   await Task.Run(async () =>
   {
      while (!cancellationToken.IsCancellationRequested)
      {
          DoSomething();
          await Task.Delay(10000, cancellationToken);
      }
   });

当你想停止线程时,只需中止令牌:

cancellationTokenSource.Cancel();

相关问题