【问题标题】:Why does Thread.Sleep work but Task.Delay does not?为什么 Thread.Sleep 有效但 Task.Delay 无效?
【发布时间】:2020-05-14 23:58:38
【问题描述】:

在我的代码中,我假设outerFlag 将在innerFlag 之后被击中,但实际上它运行起来就像火了然后忘记了(innerFlagouterFlag 之后被击中)。当我使用 Thread.Sleep 而不是 Task.Delay 时,标志会按正确的顺序命中。

代码如下:

[Test]
public async Task Test() {

    bool innerFlag = false;
    bool outerFlag = false;

    await Lock(async () => {
        await Task.Delay(2000);
        innerFlag = true;
    });

    outerFlag = true;

    Thread.Sleep(1000);
    Thread.Sleep(2500);
}

private Task Lock(Action action) {
    return Task.Run(action);
}

我还注意到,当我调用 Task.Delay 并设置 innerFlag 而不使用 Lock 方法但通过直接 lambda 时,它按预期工作。

有人可以解释这种行为吗?

【问题讨论】:

  • 那是因为Lock 接受了一个委托,在你的情况下这个委托返回一个任务。但是您不会等待该任务,因此这是一项“一劳永逸”的任务。基本上你只需通过await Lock(...) 爆炸,并且委托内部的 2 秒延迟将等待然后将 innerFlag 设置为 true,但你已经忽略了该任务并继续执行。
  • 如果你把Lock改成private async Task Lock(Func<Task> func) { await func(); },效果可能会更好。
  • 这能回答你的问题吗? await Task.Delay() vs. Task.Delay().Wait()
  • 当然它运行起来就像火了然后忘记了——你将Task-returning 方法传递给吃Action 的东西,这意味着这样产生的任务被忽略了。从本质上讲,你得到的正是你所要求的。 (请注意,如果您想要实际的锁定/排除,这绝对不是,您想使用SemaphoreSlimAsyncLock 之类的东西 - 不要尝试使用您自己的布尔标志在这里重新发明轮子,因为这几乎当然会遇到一些微妙的错误。)
  • @defaultlocale 不,你的问题没有回答这个问题

标签: c# .net asynchronous async-await task


【解决方案1】:

您的Lock 方法不理解异步委托,因此您尝试作为参数传递的异步委托被视为async void。异步 void 在各种方面都是有问题的,should be avoided,除非它们被用于其预期目的,作为事件处理程序。

为确保您的方法理解异步委托,您必须创建一个接受 Func<Task> 作为参数的重载:

private Task Lock(Func<Task> func)
{
    return Task.Run(func);
}

注意func 参数可以直接传递给Task.Run,因为这个方法也理解异步委托。并非所有内置方法都理解异步委托,著名的例子有 Task.Factory.StartNewParallel.ForEach。每次在委托中添加 async 修饰符时都必须小心。您必须确保被调用的方法理解异步委托,否则您最终可能会遇到异步空洞及其造成的破坏。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 2020-10-07
    • 2023-03-30
    • 2013-05-13
    • 2011-09-06
    • 2020-02-19
    相关资源
    最近更新 更多