You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中async/await延迟时长为何影响执行行为?是否存在竞态条件?

问题:修改Task.Delay时长为何影响异步任务的并行执行?

以下代码原本预期两个Service实例同时启动:

List<Service> services = [ new Service(), new Service() ];

foreach (Service service in services)
{
    Task task = service.StartAsync();
}

Console.ReadLine();

class Service
{
    public async Task StartAsync()
    {
        await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("HH:mm:ss")}\tStarted");

        await Task.Delay(TimeSpan.FromSeconds(1));

        //Simulate some synchronous work.
        Thread.Sleep(10_000);

        await Console.Out.WriteLineAsync($"{DateTime.Now.ToString("HH:mm:ss")}\tFinished");
    }
}

原始输出(延迟1秒):

20:32:25     Started
20:32:25     Started
20:32:35     Finished
20:32:35     Finished

但将延迟从TimeSpan.FromSeconds(1)改为TimeSpan.FromMicroseconds(1)后,输出变为:

20:40:10     Started
20:40:20     Finished
20:40:20     Started
20:40:30     Finished

请问为何修改延迟时长会影响执行行为?是否存在竞态条件?


解答

这不是竞态条件,核心原因是**Task.Delay的时长会影响异步方法的调度行为**,结合控制台程序的线程调度规则导致了差异:

  1. 异步方法的执行逻辑差异:

    • 调用StartAsync后,先执行await Console.Out.WriteLineAsync,这一步异步完成后方法挂起,主线程回到循环启动第二个Service的StartAsync,所以两个Started会同时输出。
    • 执行await Task.Delay时:
      • 当延迟为1秒时,Task.Delay会创建真正的异步延迟操作,方法挂起,线程回到线程池。此时两个任务都处于等待状态,延迟结束后线程池会分配两个独立线程执行后续的Thread.Sleep(10000),两个同步阻塞任务并行进行,最终同时完成。
      • 当延迟为1微秒时,Task.Delay会直接返回一个已完成的Task(系统因延迟过短,内部优化为同步完成)。此时await不会挂起方法,直接执行后续的Thread.Sleep(10000),这会阻塞当前主线程。
  2. 控制台主线程的阻塞影响:
    控制台程序的初始任务启动依赖主线程,第一个StartAsync执行到Thread.Sleep时阻塞了主线程,导致循环无法及时启动第二个StartAsync。直到第一个任务的Thread.Sleep结束,主线程才会回到循环启动第二个任务,最终表现为串行执行。

简单总结:短到极致的Task.Delay被优化为同步执行,后续阻塞代码占用了启动任务的主线程,使得第二个任务无法及时启动;正常时长的Task.Delay触发异步挂起,释放主线程启动第二个任务,从而实现并行。


内容的提问来源于stack exchange,提问作者Sandy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:40:19