.NET 6推出了PeriodicTimer.

我需要每一分钟都做些事情,在最重要的时刻.例如:09:23:0009:24:0009:25:00...

但是用一分钟的时间-new PeriodicTimer(TimeSpan.FromMinutes(1))-从09:23:45开始,我会在:09:24:4509:25:4509:26:45...

所以这取决于开始时间.

我的变通方法是1秒,判断当前时间是否等于0秒.另一种解决方法是等待下一分钟,然后启动计时器.这两种方法都有效,但都很精细,分辨率太高.

是否有内置或更好的方式在top of the minute而不是启动后一分钟触发?

推荐答案

标准中没有可用的内容.NET库,具有此功能.而且我不认为它很快会被加入.我的建议是使用第三方Cronos库,它可以很好地处理时间间隔的计算.您可以找到斯蒂芬·克利里的用法示例here.这个库只计算时间间隔,不做任何调度.

如果您想获得更多乐趣,可以将Cronos库的功能包含在定制的PeriodicTimer类组件中,如下所示:

using Cronos;

public sealed class CronosPeriodicTimer : IDisposable
{
    private readonly CronExpression _cronExpression;
    private readonly CancellationTokenSource _cts;
    private static readonly TimeSpan _minDelay = TimeSpan.FromMilliseconds(500);

    public CronosPeriodicTimer(string expression, CronFormat format)
    {
        _cronExpression = CronExpression.Parse(expression, format);
        _cts = new();
    }

    public async ValueTask<bool> WaitForNextTickAsync(
        CancellationToken cancellationToken = default)
    {
        if (_cts.IsCancellationRequested) return false;
        CancellationTokenSource linkedCts = null;
        CancellationToken token = _cts.Token;
        if (cancellationToken.CanBeCanceled)
        {
            cancellationToken.ThrowIfCancellationRequested();
            linkedCts = CancellationTokenSource
                .CreateLinkedTokenSource(_cts.Token, cancellationToken);
            token = linkedCts.Token;
        }
        try
        {
            DateTime utcNow = DateTime.UtcNow;
            DateTime? utcNext = _cronExpression.GetNextOccurrence(utcNow + _minDelay);
            if (utcNext is null)
                throw new InvalidOperationException("Unreachable date.");
            TimeSpan delay = utcNext.Value - utcNow;
            Debug.Assert(delay > _minDelay);
            try
            {
                await Task.Delay(delay, token).ConfigureAwait(false);
                return true;
            }
            catch (OperationCanceledException)
            {
                cancellationToken.ThrowIfCancellationRequested();
                return false;
            }
        }
        finally { linkedCts?.Dispose(); }
    }

    public void Dispose() => _cts.Cancel();
}

除了构造函数之外,CronosPeriodicTimer类与PeriodicTimer类具有相同的API.您可以这样使用它:

var timer = new CronosPeriodicTimer("0 * * * * *", CronFormat.IncludeSeconds);
//...
await timer.WaitForNextTickAsync();

表达式0 * * * * *表示"on the 0 (zero) second of every minute, of every hour, of every day of the month, of every month, and of every day of the week."

您可以找到关于Cron表达式here格式的详细文档.


Note:为了处理Task.Delay比目标DateTime完成earlier的场景,WaitForNextTickAsync的延迟至少为500毫秒.我无法通过实验实现这种情况.Task.Delay总是在几毫秒after内完成我机器中的目标DateTime.尽管如此,为了防止计时器两次错误滴答作响的可能性微乎其微,还是应该包括这一安全措施.此功能在实践中不应有任何明显的效果.

Csharp相关问答推荐

图形.DrawString奇怪异常的字距调整

WPF Windows初始化正在锁定. Net 8中分离的线程

如果属性名为xyz,我需要使用System.Text.Json修改字符串类型的值""<>

此反射有什么问题.是否发送值转换委托?

Azure DEVOPS找不到定制的Nuget包

使用两个不同的枚举作为Switch语句中的CASE生成唯一值

SortedSet.Remove()不会删除SortedSet.Min返回的元素

C#带主体的主构造函数?

VS 2022 for ASP.NET Core中缺少自定义项模板

为什么此名称不再被识别?名称不存在于当前上下文中?

在集成测试中可以在模拟InMemory数据库中设定数据种子

如何使用EPPlus C#在单个单元格中可视化显示多行文字

我应该为C#12中的主构造函数参数创建私有属性吗?

为什么我在使用有效令牌的情况下仍未获授权?

多个参数的最小API删除

如何在Cake脚本中设置MSBuild.exe的绝对路径

当要删除的子模型没有父模型的1:多属性时,如何告诉实体框架设置1:1 FK条目?

.NET EF Core Automapper项目到筛选不起作用

在Visual Studio 2022中查找Xamarin模板时遇到问题

如何将行添加到DataGrid以立即显示它?