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.Delay 的定时精度及其影响因素的更多相关文章

  1. Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别

    Task C# 多线程和异步模型 TPL模型   Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...

  2. 15.3 Task Task.Yield和Task.Delay说明

    https://blog.csdn.net/hurrycxd/article/details/79827958 书上看到一个Task.Yield例子,Task.Yield方法创建一个立即返回的awai ...

  3. 理解Task和和async await

    本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...

  4. Thread.Sleep vs. Task.Delay

    We use both Thread.Sleep() and Task.Delay() to suspend the execution of a program for some given tim ...

  5. .Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法

    前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...

  6. Task.Delay() 和 Thread.Sleep() 区别

    1.Thread.Sleep 是同步延迟,Task.Delay异步延迟. 2.Thread.Sleep 会阻塞线程,Task.Delay不会. 3.Thread.Sleep不能取消,Task.Dela ...

  7. Task.Delay方法的2个应用实例,单元测试等待,限时限次下载远程资源

    如果想让程序异步等待一段时间,可以考虑使用Task.Delay方法. 比如,在单元测试中模拟一个异步操作. static async Task<T> DelayedResult<T& ...

  8. Thread.Sleep(1000) 、Task.Delay(1000).Wait() 区别

    public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken){    if (millise ...

  9. task.delay 和 thread.sleep

    1.Thread.Sleep 是同步延迟. Task.Delay异步延迟. 2.Thread.Sleep 会阻塞线程,Task.Delay不会. 3.Thread.Sleep不能取消,Task.Del ...

  10. C#异步延迟Task.Delay

    一. 1.Task.Delay实质是创建一个任务,再任务中开启一个定时间,然后延时指定的时间2.Task.Delay不和await一起使用情况,当代码遇到Task.Delay一句时,创建了了一个新的任 ...

随机推荐

  1. 3.1 migration to 5.0

    记入我遇到的问题 : 1. localizer.WithCulture 废弃了 https://github.com/dotnet/aspnetcore/issues/7756 其实讨论很久了, 只是 ...

  2. LinerProgression

    手动实现线性回归 点击查看代码 import torch import pandas as pd import numpy as np import matplotlib.pyplot as plt ...

  3. 命令行gcc -v和g++ -v输出版本不一致

    命令行gcc -v和g++ -v输出版本不一致 前言:本文初编辑于2024年1月30日 CSDN主页:https://blog.csdn.net/rvdgdsva 博客园主页:https://www. ...

  4. 【赵渝强老师】使用Docker UI

    Docker提供一个平台来把应用程序当作容器来打包.分发.共享和运行,它已经通过节省工作时间来拯救了成千上万的系统管理员和开发人员.Docker不用关注主机上运行的操作系统是什么,它没有开发语言.框架 ...

  5. Oracle ASM磁盘组配置、日常运维、故障处理等操作资料汇总

    ASM(自动存储管理)在数据库中是非常重要的组成部分,它可以为磁盘提供统一的存储管理.提高磁盘访问的性能和可用性.简化管理复杂度,从而为数据库的运行提供更好的支持. 这里就为大家整理了墨天轮数据社区上 ...

  6. js中 操作符new 的作用和含义

    作用:通过构造函数创建实例对象 :通过 new 出来的实例可以访问构造函数的属性和方法 :

  7. (系列七).net8 Aop切面编程

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  8. KubeSphere 开源社区 2022 年度回顾与致谢

    2022 年,国内的云原生技术生态日趋完善,细分技术项目也不断涌现,形成了完整的支撑应用云原生化的全生命周期技术体系.基础设施即代码.微服务.Serverless 等技术,促使基础设施资源向更加灵活弹 ...

  9. SpringBoot2.0 整合 JWT 框架后台生成token

    一.传统Session认证 1.1.认证过程 1.用户向服务器发送用户名和密码.2.服务器验证后在当前对话(session)保存相关数据.3.服务器向返回sessionId,写入客户端 Cookie. ...

  10. [Flink/FlinkCDC] 实践总结:Flink 1.12.6 升级 Flink 1.15.4

    Flink DataStream/API 依赖模块的变化 版本变化 flink.version : 1.12.6 => 1.15.4 flink.connector.version : 1.12 ...