【问题标题】:When is it asynchronous and when is synchronous什么时候异步,什么时候同步
【发布时间】:2021-11-08 09:07:47
【问题描述】:

阅读了/很多/关于异步等待模式的文档后,我认为我对该模式有了很好的掌握。我一直阅读有关异步的所有内容,然后阅读另一篇文章,其中提到“如果它在 50 毫秒内运行,则不要异步它”。似乎存在相互矛盾的信息和/或意见,我已经设法让自己感到困惑。我还读到 Task.Yield() 将强制异步修饰方法异步运行。

给定以下代码:

    static async Task MethodA() {
        await MethodB().ConfigureAwait(false);
    }

    static async Task MethodB() {
        await Task.Yield();
        MethodC();
    }

    static void MethodC() {
        // do some synchronous stuff
    }

    static async Task Main(string[] args) {
        var task1 = Task.Run(() => MethodA().ConfigureAwait(false));
        var task2 = Task.Run(() => MethodB().ConfigureAwait(false));

        await Task.WhenAll(new List<Task>() { task1, task2 });
    }

MethodC 是同步运行还是异步运行,我假设它是异步的,因为它是从异步方法调用的。另外,Task.Yield 是否必要?

老实说,这让我很头疼,我读过的每一篇文章都深入探讨了异步等待模式以及原因和原因,只是增加了问题的复杂性。只是在寻找一个简单的答案。

【问题讨论】:

  • “我还读到 Task.Yield() 将强制异步修饰的方法异步运行”请告诉我你在哪里读到的。
  • Task.Yield 可用于分解异步操作,否则可能会阻塞 调用者的线程,即您想推迟它和/或将它传递给线程池;因为MethodB() 是通过Task.Run 调用的,它已经在线程池中,因此使用Task.Yield 没有任何用处。还需要注意的是,异步和并发是非常不同的概念。你的task1/task2 设置演示了并发性,而不是真正的async - 因为这里没有真正的真正的async 发生
  • @IanKemp 至少是真的;根据定义,Task.YieldIsCompleted 返回报告false 的内容,并且通常会通过线程池重新激活,因此:从这个角度来看:是的,它将采用一个否则会同步运行的方法(意思是:它会返回一个已经完成的等待对象),而不是返回一个不完整的等待对象,这意味着:它将被归类为完全异步。请注意,这并不一定意味着这些术语可能意味着 OP。
  • 更简单的接受是,如果您调用返回Task的方法,则该任务可能会或可能不会在方法调用返回时完成,您需要使用Task来确定当它完成时。 方法如何实现,从调用代码的角度来看,它可能会或可能不会安排Task 稍后完成在很大程度上无关

标签: c# .net-core async-await


【解决方案1】:

MethodC 是同步运行还是异步运行

同步。它有一个同步签名,所以它会一直同步运行。

我假设是异步的,因为它是从异步方法调用的

调用它的上下文与MethodC 的运行方式无关。

Task.Yield 是必需的

它会强制MethodBMethodC 运行之前产生Task,但这在MethodC 完成之前是不完整的,并且因为MethodC 是同步的(不释放线程)它什么也没做有用。

只是在寻找一个简单的答案

async 冒泡 调用堆栈,并且因为您没有在此处使用 async 方法(MethodC 是同步的)您根本不应该使用 async

【讨论】:

    【解决方案2】:

    此答案包含一般信息和建议,它不关注您发布的代码。

    1. 在学习 async-await 时感到困惑是可以的。恕我直言,在不经历混淆阶段的情况下完全理解 async-await 几乎是不可能的。
    2. await Task.Yield()await Task.Delay(1)await Task.Run(() =&gt; { }).ConfigureAwait(false) 是肮脏的 hack,人们有时会因为不存在强制切换到 ThreadPool 上下文的显式方法而感到沮丧(在标准图书馆)。 SwitchTo 方法存在于一些预发布的 .NET 版本中,然后由于技术原因将其删除,然后技术原因被消除,重新引入该方法从未成为足够高的优先级,因此没有发生。 It may happen in the future,但不要屏住呼吸。如果需要,它是 Microsoft.VisualStudio.Threading 包中的 currently available
    3. 您不需要任何这些技巧即可成功编写异步等待代码。如果您想将工作交给ThreadPoolTask.Run 方法可以完美地完成这项工作。
    4. 将工作卸载到ThreadPool 应该在“应用程序代码”级别完成,而不是在“库代码”级别。您可以阅读this 文章,了解为什么为同步方法公开异步包装器不是一个好主意。

    【讨论】:

    • 我相信 SwitchTo 被删除是出于可用性原因,而不是纯粹的技术原因。而且可用性原因仍然存在:具体来说,在带有SwitchTo 的方法中,catchfinally 块在哪个上下文中运行?它可以是任一上下文,这就是为什么不建议以相同方法切换上下文的原因。当然,现在可以切换回来(消除了技术原因),但记住您甚至必须这样做(仍然存在可用性原因)仍然是一种精神负担。
    猜你喜欢
    • 2015-04-05
    • 2012-06-28
    • 2019-08-27
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多