首页 > 其他分享 >深入理解 Task.Delay 的定时精度及其影响因素

深入理解 Task.Delay 的定时精度及其影响因素

时间:2024-12-11 14:53:43浏览次数:3  
标签:Task delayMilliseconds System Delay Stopwatch using 定时

1. 原因

在日常开发中,Task.Delay 是一个常用的异步延迟方法。然而,Task.Delay 的定时并不总是非常准确。例如:

  • 系统负载 Task.Delay 的定时精度可能会受到系统负载的影响。如果系统负载较高,CPU 和其他资源被大量占用,任务调度可能会被延迟,从而导致 Task.Delay 的实际延迟时间超过预期。

  • 任务调度 Task.Delay 是基于任务调度器的,而任务调度器的调度精度可能会受到操作系统的影响。操作系统的任务调度器会根据系统的整体负载和优先级来调度任务,这可能会导致 Task.Delay 的实际延迟时间不够精确。

  • 定时器精度 Task.Delay 使用的是系统定时器,而系统定时器的精度可能会受到硬件和操作系统的限制。不同的操作系统和硬件平台可能会有不同的定时器精度,这也会影响 Task.Delay 的精度。

  • 电源管理 在某些情况下,电源管理策略(如节能模式)可能会影响任务调度和定时器的精度。例如,在节能模式下,CPU 可能会降低频率或进入休眠状态,从而影响 Task.Delay 的精度。

  • GC(垃圾回收) 在 .NET 中,垃圾回收(GC)可能会暂停所有托管线程,从而影响 Task.Delay 的精度。如果在 Task.Delay 期间发生了垃圾回收,实际的延迟时间可能会超过预期。

以下是一个示例代码,展示了如何使用 Task.DelayStopwatch 来测量实际延迟时间,以便更好地理解 Task.Delay 的精度:

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

public class Program
{
    public static async Task Main(string[] args)
    {
        int delayMilliseconds = 1000; // 1秒

        Stopwatch stopwatch = Stopwatch.StartNew();
        await Task.Delay(delayMilliseconds);
        stopwatch.Stop();

        Console.WriteLine($"Expected delay: {delayMilliseconds} ms");
        Console.WriteLine($"Actual delay: {stopwatch.ElapsedMilliseconds} ms");
    }
}

在这个示例中,我们使用 Stopwatch 来测量 Task.Delay 的实际延迟时间。你可以运行这个示例代码,观察实际延迟时间与预期延迟时间之间的差异。

2. 更加精准的解决方案对比

为了实现更高精度的定时,我们可以使用 System.Threading.TimerSystem.Diagnostics.Stopwatch。以下是这两种方法的对比示例:

2.1. 使用 System.Threading.Timer

System.Threading.Timer 提供了更高精度的定时控制,可以避免 Task.Delay 的一些不准确性。

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

public class TimerExample
{
    public async Task ExecuteWithTimer(int delayMilliseconds)
    {
        var tcs = new TaskCompletionSource<bool>();
        using (var timer = new Timer(_ => tcs.SetResult(true), null, delayMilliseconds, Timeout.Infinite))
        {
            await tcs.Task;
        }
    }
}

2.2. 使用 System.Diagnostics.Stopwatch

Stopwatch 可以用来精确测量时间间隔,并结合 Task.Delay 实现更高精度的定时控制。

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

public class StopwatchExample
{
    public async Task ExecuteWithStopwatch(int delayMilliseconds)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
        while (stopwatch.ElapsedMilliseconds < delayMilliseconds)
        {
            await Task.Delay(1); // 短暂延迟以避免忙等待
        }
    }
}

2.3. 使用示例

public class Program
{
    public static async Task Main(string[] args)
    {
        int delayMilliseconds = 1000; // 1秒

        // 使用 Task.Delay
        Stopwatch stopwatch1 = Stopwatch.StartNew();
        await Task.Delay(delayMilliseconds);
        stopwatch1.Stop();
        Console.WriteLine($"Task.Delay - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch1.ElapsedMilliseconds} ms");

        // 使用 System.Threading.Timer
        var timerExample = new TimerExample();
        Stopwatch stopwatch2 = Stopwatch.StartNew();
        await timerExample.ExecuteWithTimer(delayMilliseconds);
        stopwatch2.Stop();
        Console.WriteLine($"Timer - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch2.ElapsedMilliseconds} ms");

        // 使用 System.Diagnostics.Stopwatch
        var stopwatchExample = new StopwatchExample();
        Stopwatch stopwatch3 = Stopwatch.StartNew();
        await stopwatchExample.ExecuteWithStopwatch(delayMilliseconds);
        stopwatch3.Stop();
        Console.WriteLine($"Stopwatch - Expected delay: {delayMilliseconds} ms, Actual delay: {stopwatch3.ElapsedMilliseconds} ms");
    }
}

3. 总结

虽然 Task.Delay 在大多数情况下是足够准确的,但它确实可能受到系统负载、任务调度、定时器精度、电源管理和垃圾回收等因素的影响,导致定时不够精确。通过使用 System.Threading.TimerSystem.Diagnostics.Stopwatch,我们可以实现更高精度的定时控制。

标签:Task,delayMilliseconds,System,Delay,Stopwatch,using,定时
From: https://www.cnblogs.com/Tangtang1997/p/18599614

相关文章

  • kali 定时更换桌面背景以及登录背景
    目录标题桌面右键选择桌面设置背景文件目录更改桌面背景更改登录界面背景桌面右键选择桌面设置背景文件目录/usr/share/backgrounds在这个目录下可以看到登录界面背景,桌面背景,以及命令控制面板背景有以下三个目录kali:登录背景图片kali-16x9:桌面背景xfc......
  • 达梦归档定时备份和定时删除
    达梦归档定时备份和定时删除1配置归档alterdatabasemount;alterdatabaseaddarchivelog'dest=C:\dmdbms\data\dmarch,TYPE=local,FILE_SIZE=1024,SPACE_LIMIT=40000';alterdatabasearchivelog;alterdatabaseopen;2打开代理--归档备份(每天凌晨一点备份归档文件)ca......
  • 五、locust -- 顺序执行 SequentialTaskSet
    #*_*coding:utf-8*_*#@Author:zybimportqueueq=queue.Queue()foriinrange(100):q.put(i)fromlocustimportFastHttpUser,task,between,SequentialTaskSetclassMySequentialTasks(SequentialTaskSet):@taskdeftask_one(self):......
  • Spring Boot集成ShedLock实现分布式定时任务
    1、什么是ShedLock?ShedLock是一个Java库,通常用于分布式系统中,确保定时任务(ScheduledTasks)在集群环境下只被某一个实例执行一次。它通过在共享资源(例如数据库或分布式缓存)中添加锁的方式,避免多个实例同时执行相同的任务ShedLock的工作原理1.分布式锁:在任务开始时,She......
  • Apache DolphinScheduler 限制秒级别的定时调度
    背景ApacheDolphinScheduler定时任务配置采用的7位Crontab表达式,分别对应秒、分、时、月天、月、周天、年。在团队日常开发工作中,工作流的定时调度一般不会细化到秒级别。但历史上出现过因配置的疏忽大意而产生故障时间,如应该配置每分钟执行的工作流被配置长了每秒执行,造......
  • kali 定时更换桌面背景以及登录背景
    桌面右键选择桌面设置背景文件目录/usr/share/backgrounds在这个目录下可以看到登录界面背景,桌面背景,以及命令控制面板背景有以下三个目录kali:登录背景图片kali-16x9:桌面背景xfce:命令控制面板更改桌面背景更改桌面背景,只需要在刚才目录下放入自己喜欢的图片,然后......
  • AEC论文解读 -- MULTI-TASK DEEP RESIDUAL ECHO SUPPRESSION WITH ECHO-AWARE LOSS
    一、技术解读1.1数据处理的方式  使用3带有限脉冲响应(FIR)滤波器组和离散余弦变换(DCT)调制,将全频带(48kHz)信号分解为子带,并且只处理宽带(16kHz)信号,最终全频带信号通过平均增益控制合成。1.1.1信号分解与DCT调制  在信号处理的初始阶段,使用带通FIR滤波器将全频带信号分......
  • STM32中使用低功耗定时器延时
    此篇文章在2022年5月19日被记录上文说了STM32L4的几种低功耗模式,将其应用起来作为一个低功耗的延时方案。为什么使用低功耗定时器,在追求长时间续航时,单片机有时需要切换到低功耗模式或者停止模式下,在这种模式下,系统主时钟关闭,有一些依赖于系统主时钟的应用程序,可能会发生出现......
  • Mvc项目利用Quartz实现定时调度Demo
    1、创建MVC项目(StudyQuartz),如下图显示 2、mvc项目安装Quartz库,有两种方式1)通过“程序包管理控制台”(视图-->其他窗口-->程序包管理器) 输入“Install-PackageQuartz”完成安装2)通过NuGet包管理器 在浏览输入“Quartz”,下载最新版本到项目中 3、利用Quartz实现定......
  • STM32单片机芯片与内部13 TIM-通用定时器TIM2345 高级定时器TIM18-定时计数功能、库函
    目录一、通用定时器库函数工程模板1、TIM_TimeBaseInitTypeDef2、时钟3、初始化4、中断服务函数二、通用定时器库函数API1、初始化封装2、中断服务函数封装三、高级定时器库函数工程模板1、TIM_TimeBaseInitTypeDef2、时钟3、初始化4、中断服务函数四、高级定时......