C# 在同步上下文中调用异步函数时释放调用线程

C# 在同步上下文中调用异步函数时释放调用线程,c#,multithreading,.net-core,async-await,C#,Multithreading,.net Core,Async Await,在c#(dotnet core 3.1)中,似乎不可能在不阻塞原始线程的情况下从非异步方法调用异步函数。为什么? 代码示例: public async Task myMethodAsync() { await Task.Delay(5000); } public void callingMethhod() { myMethodAsync().Wait(); // all flavours of this expression, like f.ex. .Result seem to be

在c#(dotnet core 3.1)中,似乎不可能在不阻塞原始线程的情况下从非异步方法调用异步函数。为什么?

代码示例:

public async Task myMethodAsync() {
  await Task.Delay(5000);
}

public void callingMethhod() {
  myMethodAsync().Wait(); // all flavours of this expression, like f.ex. .Result seem to be blocking the calling thread
}

在异步方法完成之前释放调用线程,然后从异步方法继续执行的技术限制是什么?这在技术上是不可能的吗?

是的,这是意料之中的。这就是
任务
等概念存在的原因。如果可以做你想做的事情:他们不需要这样做——我们可以挥动魔杖,一切都是异步的。等待类型的全部要点是,在允许后续继续的同时释放调用线程是困难的,需要调用代码的协调和参与;从什么叫它;etc-一直到运行线程的堆栈。您的
调用方法
代码是同步的:它只能做几件事:

  • 它可以运行到完成——意思是:它必须阻塞
  • 它可以抛出异常
其影响是
async
/
await
具有传染性;任何涉及可等待项的内容都需要是可等待的,这意味着:您通常只会从
callingmethod
调用
myMethodAsync
,该方法返回一个
[Value]Task[]
,可能是
async
wait
(尽管在所示示例中这些位不是严格必需的)

在c#(dotnet core 3.1)中,似乎不可能在不阻塞原始线程的情况下从非异步方法调用异步函数。为什么

因为这就是同步的意思。同步意味着它阻塞线程直到方法完成

能够释放调用线程直到异步方法完成,然后从那里继续执行的技术限制是什么?这在技术上是不可能的吗

如果您的调用方法想要释放其线程,则将其设置为异步。异步方法能够释放其调用线程。这里没有什么不可能的事情;它已通过
async
wait
解决

现在,如果你问“为什么不能每个方法都隐式地是
异步的
”,那么这在理论上是可能的,但它会引起几个主要问题。由于向后兼容性的原因,它永远不能在C中实现。立即想到的两个问题是:

  • 有限的互操作。不可能使用“everything is
    async
    ”语言与任何使用老式线程(Win32互斥等)的东西进行互操作
  • 意外的并发性。在很多情况下,开发人员假设代码是同步的。如果每个方法都可能是异步的,那么即使像
    var x=this.\u list[0];this.\u list.RemoveAt(0);
    这样简单的代码也不再安全

  • 我很想听到更多关于为什么会出现这种情况的信息。也就是说,为什么它必须是这样,为什么允许继续是“困难的”?@JKJ简单地说:那会是什么样子?实际会发生什么?如果异步的事情不完整,调用堆栈下一步会去哪里?myMethodAsync的结果会怎么样(这是否意味着返回值,或异常)?谁调用代码>调用MyHOOD < /代码>还知道事情正在解除?如果是这样?它只能看到一个<代码>无效>代码>它对任务一无所知。在调用语义学方面:等待在同步流中间……无法完成;您可以阻止(通过<代码>。
    .Result
    ),但您确实不应该这样做(这显然是一个非常糟糕的主意)@JKJ将其放在上下文中-它可能有助于说明异步有多复杂-这里有一些类似于您的代码,但有两个
    async
    方法;现在查看右侧窗格,它显示了编译器为使其工作所做的工作-它确实非常复杂,因为调度异步连续性本质上是一个复杂的过程ed Affance:@JKJ隐藏线程状态非常困难-你需要理解很多调用语义-你不能只是中断线程,伪造数据,然后说“你完成了,现在你正在做其他事情”。存储和展开线程状态是Waitiers启用的-同样:这就是概念存在的原因。如果没有这种API,这不是你可以神奇地完成的事情。@JKJ还请记住:启用Waitier所需的机制不是免费的-仅仅说一句话对性能会有很大的危害编译器现在神奇地实现了异步;如果返回void,则返回ValueTask等"如果你明白我的意思的话,它也不会是二进制兼容的-它会打破世界。这甚至是不可能的:在
    async
    方法中,有些事情你不能做,因为状态无法存储-例如,任何涉及ref局部变量的事情-或者因为线程语义-任何使用
    锁的事情,例如appr值得赞赏的是,在我看来,通过禁止意外的异步性来保证同步性,从这个角度来看,可以被视为更理想的特性,而不是技术限制。