C# 异步能保证什么?

C# 异步能保证什么?,c#,async-await,C#,Async Await,如果一个方法被标记为async,那么它有任何保证吗 如果没有,调用异步方法以确保非阻塞行为的最佳实践是什么?尤其是打电话给第三方图书馆时 较长版本: 到目前为止,我假设等待异步方法可以保证在正确实现的情况下不会阻塞线程。然而,我最近也碰巧遇到了。 在本例中,非阻塞行为取决于EF提供程序。由于UI层不应该真正了解或关心DB提供程序的实现细节,我认为在Task.Run中包装所有调用是有意义的,以确保不阻塞bahaviour。但是,除了UI层之外,我在任何地方都不需要异步,这让我对始终使用异步的原理产

如果一个方法被标记为async,那么它有任何保证吗

如果没有,调用异步方法以确保非阻塞行为的最佳实践是什么?尤其是打电话给第三方图书馆时

较长版本:

到目前为止,我假设等待异步方法可以保证在正确实现的情况下不会阻塞线程。然而,我最近也碰巧遇到了。
在本例中,非阻塞行为取决于EF提供程序。由于UI层不应该真正了解或关心DB提供程序的实现细节,我认为在Task.Run中包装所有调用是有意义的,以确保不阻塞bahaviour。但是,除了UI层之外,我在任何地方都不需要异步,这让我对始终使用异步的原理产生了疑问。

将方法标记为异步实际上没有任何保证,当然也不能保证该方法将异步运行

但我不认为这些保证是:

允许在方法内使用wait关键字,但不强制执行它!。 它强制方法的返回类型为void、Task或Task。在最后两种情况下,它使编译器能够为您生成知道如何在任务中设置返回值的代码,或者为您透明地传播未处理的异常。 但重点是:它不能保证方法将以异步方式编码。你可以自己检查一下。编写一个普通方法,将其标记为async,然后忘记等待方法内部的任何内容。你应该从编译器那里得到一个关于该方法是同步的警告,但这是允许的

我认为你会发现,通过以下步骤来澄清你的一些疑问是有用的:

对方法使用async关键字是否会强制该方法的所有调用都是异步的

不可以。当您调用标记为async的方法时,它开始在当前线程上同步运行。因此,如果您有一个返回void的同步方法,并且您所做的所有更改都是将其标记为async,那么该方法的调用仍将同步运行。无论是将返回类型保留为void还是将其更改为Task,这都是正确的。类似地,如果有一个同步方法返回一些TResult,您所做的只是将其标记为async并将返回类型更改为Task,那么该方法的调用仍将同步运行

将方法标记为“异步”并不影响该方法是同步运行还是异步运行完成


将一个方法标记为异步实际上并没有任何保证,当然也不能保证该方法将异步运行

但我不认为这些保证是:

允许在方法内使用wait关键字,但不强制执行它!。 它强制方法的返回类型为void、Task或Task。在最后两种情况下,它使编译器能够为您生成知道如何在任务中设置返回值的代码,或者为您透明地传播未处理的异常。 但重点是:它不能保证方法将以异步方式编码。你可以自己检查一下。编写一个普通方法,将其标记为async,然后忘记等待方法内部的任何内容。你应该从编译器那里得到一个关于该方法是同步的警告,但这是允许的

我认为你会发现,通过以下步骤来澄清你的一些疑问是有用的:

对方法使用async关键字是否会强制该方法的所有调用都是异步的

不可以。当您调用标记为async的方法时,它开始在当前线程上同步运行。因此,如果您有一个返回void的同步方法,并且您所做的所有更改都是将其标记为async,那么该方法的调用仍将同步运行。无论是将返回类型保留为void还是将其更改为Task,这都是正确的。类似地,如果有一个同步方法返回一些TResult,您所做的只是将其标记为async并将返回类型更改为Task,那么该方法的调用仍将同步运行

将方法标记为“异步”并不影响该方法是同步运行还是异步运行完成

我认为在Task.Run中包装所有调用是有意义的,以确保不阻塞bahaviour

我想没有。虽然您确实没有保证,但大多数异步方法实际上是异步的。所以我认为应该将调用包装在Task中。只有在您发现某个方法或它的某个实现实际上正在阻塞后才能运行,但不能在之前运行

我认为在Task.Run中包装所有调用是有意义的,以确保不阻塞bahaviour

我想没有。虽然您确实没有保证,但大多数异步方法实际上是异步的。因此,我认为您应该将调用包装在Task中。仅运行 在您发现一个方法或它的某些实现实际上正在阻塞之后,但不是在之前

如果一个方法被标记为async,那么它有任何保证吗

不,正如其他人所说,它不能保证实际操作是异步的。这只是提示您理解可能存在异步操作。您主要依赖于实现这些异步方法的人员来实际公开真正的异步操作,而不是使用Task.Run等伪异步来欺骗您

例如,此方法是任务返回:

public Task<string> DoHeavyLiftingAsync()
{
    var tcs = new TaskCompletionSource<string>();
    string result = SomeHeavyWork();
    return tcs.SetResult(result);
}
基本上,没有执行异步操作。这会发生吗?是的,如果实现它的人没有意识到他向调用方保证了什么,这可能会发生

如果不是,那么调用异步方法的最佳实践是什么 确保无阻塞行为?尤其是打电话给第三方时 图书馆

通常,您可以信任BCL中的必须异步实现,以提供真正的异步。如果您使用的是第三方API,您可以随时使用.NET反编译器查看代码,以了解幕后的实际情况。如果执行此操作后仍不确定,则始终可以使用Task.Run结束调用。确保你把这当作最后的手段

如果一个方法被标记为async,那么它有任何保证吗

不,正如其他人所说,它不能保证实际操作是异步的。这只是提示您理解可能存在异步操作。您主要依赖于实现这些异步方法的人员来实际公开真正的异步操作,而不是使用Task.Run等伪异步来欺骗您

例如,此方法是任务返回:

public Task<string> DoHeavyLiftingAsync()
{
    var tcs = new TaskCompletionSource<string>();
    string result = SomeHeavyWork();
    return tcs.SetResult(result);
}
基本上,没有执行异步操作。这会发生吗?是的,如果实现它的人没有意识到他向调用方保证了什么,这可能会发生

如果不是,那么调用异步方法的最佳实践是什么 确保无阻塞行为?尤其是打电话给第三方时 图书馆


通常,您可以信任BCL中的必须异步实现,以提供真正的异步。如果您使用的是第三方API,您可以随时使用.NET反编译器查看代码,以了解幕后的实际情况。如果执行此操作后仍不确定,则始终可以使用Task.Run结束调用。确保将此作为最后的手段。

您必须等待对异步方法IIRC的调用。这需要一些工作才能完全部署。但有一件事它不能保证,那就是您不会从异步方法内外调用的任何代码中得到副作用。是一组关于async-await的文章。您必须等待对async方法IIRC的调用。这需要做一些工作才能完全布局。但有一件事它不能保证,那就是您不会从异步方法内外调用的任何代码中得到副作用。是关于async-await的一组文章。因此,基本上我必须将调用包装在Task中。如果我想绝对确保它将异步运行,而不是在深层某个地方阻塞,请运行。如果我这样做,除了调用部分,在任何地方使用异步都没有任何好处。当然不能保证该方法将同步运行,你是指异步运行吗?@Luka:不。这只是意味着你需要对每个异步调用做准备,以确保它的行为也符合你的预期。盲目地将所有内容包装在Task.Run调用中会破坏async/await的目的,即提供异步性,同时减少使用的线程数。@sstan-您的参数非常有意义。但是,考虑我的问题中的链接。在我看来,切换EF提供者不应该影响UI层的设计,从DevCompact切换到prod服务器是一个真实的场景。您建议如何处理这种情况?因此,基本上,我必须将调用包装在Task中。如果我想绝对确保它将异步运行,而不是在深层某个地方阻塞,请运行。如果我这样做,除了调用部分,在任何地方使用异步都没有任何好处。当然不能保证该方法将同步运行,你是指异步运行吗?@Luka:不。这只是意味着你需要对每个异步调用做准备,以确保它的行为也符合你的预期。盲目地将所有内容包装在Task.Run调用中会破坏async/await的目的,即提供异步性,同时减少使用的线程数。@sstan-您的参数非常有意义。但是,考虑我的问题中的链接。在我看来,切换EF提供者不应该影响UI层的设计,从DevCompact切换到prod服务器是一个真实的场景。你建议如何处理这种情况?