【问题标题】:Task.Delay() not behaving as expectedTask.Delay() 未按预期运行
【发布时间】:2013-09-07 11:05:24
【问题描述】:

Task.Delay() 没有按预期运行,或者我不明白它应该做什么。我正在尝试了解 C# 中的 Tasks 以及如何在我的实现中替换 Threads。

我想做的是这样的:

  • 虽然是真的
    • 打印一行
    • 等一下,
    • 如果条件满足则退出循环,否则继续循环

我已经用 Threads 很好地实现了,但所有酷孩子都说我应该使用 Task,不要碰 Thread。

所以对于我有这个的代码(忽略[Test] - 这只是一种方便的尝试方式)

    [Test]
    public void ChattyTask()
    {
        var chattyTask = new Task(ChattyWriter);
        chattyTask.Start();
        chattyTask.Wait();
    }

    public void ChattyWriter()
    {
        int count = 0;
        while (true)
        {
            var message = String.Format("Chatty Writer number {0}", count);
            Trace.WriteLine(message);
            count++;
            Task.Delay(1000);

            if (count >= 20)
            {
                break;
            }
        }
    }

当我运行它时,测试会在几毫秒内完成,而不是像我预期的那样在 20 秒内完成。如果我将Task.Delay() 替换为Thread.Sleep(),一切都会按预期进行,并且我会每秒打印一次。我尝试将asyncawait 添加到ChattyWriter() 中,但它不仅没有增加1 秒的延迟,而且只打印了一行而不是20 行。

我做错了什么?

这可能有助于描述我在做什么:我的项目使用外部 API (RESTful),在我请求一些任务发生后,我需要轮询 API 以检查任务是否已完成。外部任务可以长时间运行:1-15 分钟。所以我需要在检查完成之间有一些延迟。并且可能有许多不同的并发进程与多个外部任务一起执行。我明白,如果我在轮询时使用Thread.Sleep(),同一Thread 上的其他进程将无缘无故地被阻塞。

【问题讨论】:

    标签: c# multithreading task-parallel-library


    【解决方案1】:

    Task.Delay 返回一个您必须等待的 Task 对象。否则下面的代码将立即执行。因此,您必须将您的方法声明为async。然后就可以等待Task.Delay

    public async Task ChattyWriter()
    {
        int count = 0;
        while (true)
        {
            var message = String.Format("Chatty Writer number {0}", count);
            Trace.WriteLine(message);
            count++;
            await Task.Delay(1000);
               ...
        }
    }
    

    你必须以某种方式中断你的调用线程。单元测试将终止,后台线程也将终止。但是如果你从 UI 调用这个方法,UI 不会被阻塞。

    通过在异步方法上调用Wait,您最终会陷入死锁。请参阅here 了解更多信息。

    【讨论】:

    • 我确实试过这个,它只给了我第一行打印出来并在之后完成。
    • 这可能是因为您的主线程(测试例程)可以继续运行而您看不到其余部分,因为程序终止了。您是否从 UI 按钮启动线程,UI 仍然是响应式的。
    • 你在这方面是绝对正确的!我已将Thread.Sleep(20000) 放入测试例程中并得到了预期。
    【解决方案2】:

    Task.Delay(1000); 创建一个异步任务,该任务将在一秒钟后完成,但当前方法的执行将继续并行。

    为了等待任务先完成,你可以替换

    Task.Delay(1000);
    

    Task.Delay(1000).Wait();
    

    【讨论】:

    • @trailmax 但这会创建一个一秒钟内什么都不做的新任务。更好的方法是简单地调用 Thread.Sleep(1000)。
    • @trailmax 虽然我的帖子解释了为什么Task.Delay 不起作用,请考虑使用slfan's answer 中描述的方法,这样更有效。
    • 这两种方法之间是否存在主要区别?我总是认为async await 只是编译器的语法糖?
    • @Alberto 如果我有多个任务在同一个线程上运行(根据 MSDN,这是可能的),并调用 Thread.Sleep(),这将阻止其他任务。如果我错了,请纠正我。
    • Task.Delay 的一个优点是在传递 CancellationToken 时可以取消。
    【解决方案3】:

    Task.Delay() 实际上是创建一个任务,所以你只需要等待它。

    试试这个

            public static void ChattyWriter()
        {
            int count = 0;
            while (true)
            {
                var message = String.Format("Chatty Writer number {0}", count);
                Console.WriteLine(message);
                count++;
                var t = Task.Delay(1000);
                t.Wait();
    
                if (count >= 20)
                {
                    break;
                }
            }
        }
    

    【讨论】:

    • Task.Delay 返回 Task 的主要原因是,您可以在其上调用 await。你的答案和@w0lf的一样
    猜你喜欢
    • 2014-11-12
    • 1970-01-01
    • 2020-06-28
    • 2012-02-18
    • 2018-01-18
    • 2012-06-14
    • 2019-03-03
    • 2012-09-21
    • 2014-07-19
    相关资源
    最近更新 更多