为什么取消Task.Delay的速度很慢?

时间:2019-01-22 13:03:15

标签: c# asynchronous

我创建了一个测试程序,该程序运行1000个任务,这些任务执行Task.Delay的时间间隔为20秒到30秒。 似乎取消此操作大约需要10秒钟。

这是我的测试程序:

class Program
{
    static async Task MainAsync()
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        List<Task> allTask = new List<Task>();
        Random r = new Random(9);

        async Task SafeDelay(int delay, CancellationToken token)
        {
            try
            {
                await Task.Delay(delay, token);
            }
            catch (TaskCanceledException)
            {
            }
        }

        for (int i = 0; i < 1000; i++)
        {
            var randomDelay = r.Next(20000, 30000);
            allTask.Add(SafeDelay(randomDelay, tokenSource.Token));
            ;
        }

        Stopwatch stopwatch = new Stopwatch();

        var cancelTask = Task.Delay(1000).ContinueWith(t =>
        {
            Console.Out.WriteLine("1000ms elapsed. Cancelation request start");;
            stopwatch.Start();
            tokenSource.Cancel();
        });

        await Task.WhenAll(allTask);
        await cancelTask;

        stopwatch.Stop();

        Console.WriteLine($"Cancelation done after {stopwatch.ElapsedMilliseconds} ms");
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Started");
        Task.Run(MainAsync).GetAwaiter().GetResult();
        Console.WriteLine("End");
        Console.ReadLine();
    }
}

.NET Core 2.1的结果:

Cancelation done after 9808ms

为什么取消Task.Delay的速度如此之慢,有什么方法可以改善它?

.NET 4.7.1的我的结果:

Cancelation done after 6200ms

2 个答案:

答案 0 :(得分:5)

当我使用 F5 运行此命令时,也会得到类似的结果。
使用 Ctrl + F5 (无调试器)运行它,它会在不到50毫秒的时间内取消。

因此,您实际上是定时调试调试器的1000次执行。调试器也可能会干扰其他执行点。始终在没有调试器的发布模式下运行基准测试。

答案 1 :(得分:1)

不带问题代码的副本。按原样使用它,我得到:

Started
1000ms elapsed. Cancelation request start
Cancelation done after 38 ms
End

没有带有清理代码的副本:

static async Task SafeDelay(int delay, CancellationToken token)
{
    try
    {
        await Task.Delay(delay, token);
    }
    catch (TaskCanceledException)
    {
    }
}


private static async Task Main()
{
    //Set 1000 ms timeout
    var tokenSource = new CancellationTokenSource(1000);
    var stopwatch = Stopwatch.StartNew();

    var allTask = new List<Task>();
    Random r = new Random(9); 


    for (var i = 0; i < 1000; i++)
    {
        var randomDelay = r.Next(20000, 30000);
        allTask.Add(SafeDelay(randomDelay, tokenSource.Token));
    }
    Console.WriteLine($"All {allTask.Count} tasks running after {stopwatch.ElapsedMilliseconds} ms");

    await Task.WhenAll(allTask);
    stopwatch.Stop();

    Console.WriteLine($"Cancelation done after {stopwatch.ElapsedMilliseconds} ms");
}

这产生了:

All 1000 tasks running after 8 ms
Cancelation done after 1044 ms

CancelationTokenSource的超时时间为1000毫秒。