[英]Contractual async and sync code
有很多问题询问是否要混合异步代码和同步代码。
大多数答案都说,为异步方法公开同步包装,为同步方法公开异步包装是一个坏主意。
但是,没有一个答案能解决您必须混合使用异步和同步代码的特定情况,以及如何避免由于该问题而引起的常见陷阱。
请参见以下示例:
class Program
{
static void Main(string[] args)
{
IContract signatory = new SyncSignatory();
signatory.FullfillContractAsync().Wait();
signatory = new AsyncSignatory();
signatory.FullfillContractAsync().Wait();
}
}
using System.Threading.Tasks;
interface IContract
{
Task FullfillContractAsync();
}
using System.Threading.Tasks;
class AsyncSignatory : IContract
{
public async Task FullfillContractAsync()
{
await Task.Delay(5000);
}
}
using System.Threading;
using System.Threading.Tasks;
class SyncSignatory : IContract
{
public Task FullfillContractAsync()
{
Thread.Sleep(5000);
return Task.FromResult<object>(null);
}
}
要么:
using System.Threading;
using System.Threading.Tasks;
class SyncSignatory : IContract
{
public Task FullfillContractAsync()
{
return Task.Run(() => Thread.Sleep(5000));
}
}
在此示例中,SyncSignatory和AsyncSignatory代表两个可互换的类,因为它们执行相似的功能,但是它们以不同的方式(同步和异步)执行这些功能。
如何在避免常见陷阱的情况下混合合同同步代码和异步代码?
希望syncSig运行异步但运行同步的用户呢?
可以通过异步运行来优化syncSig的用户又如何呢?由于假设他们已经异步运行而不再进行优化,该怎么办?
这是混合异步和同步的正确方法吗?
通常是的。 如果您有一个公共接口,则其中的某些实现将能够提供同步的实现,而其他实现仅是异步的,则返回Task<T>
是有意义的。 一个简单的return Task.FromResult
可能是合适的。 但是也有例外,请参见下文。
希望syncSig运行异步但运行同步的用户呢?
接口的某些方面无法用代码表示。
如果您的IContract
要求它在返回Task
之前不阻塞调用线程的时间不超过X毫秒(以这种确切形式,这不是一个合理的硬性要求,但是您已经掌握了基本思想),并且无论如何它都会阻塞该线程,这是违反合同的行为,用户应将其作为错误报告给实施了syncSig的任何人。
如果IContract
要求不引发同步异常,则必须使用Task.FromException
或等效方法报告任何错误,而syncSig仍然引发同步异常,这也违反了合同。
除此之外,如果syncSig只是立即返回正确的结果,那么没有理由为什么会打扰任何用户。
可以通过异步运行来优化syncSig的用户又如何呢?由于假设他们已经异步运行而不再进行优化,该怎么办?
如果syncSig同步运行不会引起任何问题,那么没关系,还不需要进行优化。
如果同步运行的syncSig确实引起了问题,则基本调试工具应尽快告知开发人员syncSig引起了问题,应进行调查。
但是,没有一个答案能解决您必须混合使用异步和同步代码的特定情况,以及如何避免由于该问题而引起的常见陷阱。
那是因为这是一个坏主意。 任何混合代码本质上都应该是完全临时的,仅在过渡到异步API时才存在。
就是说,我已经写了整篇文章 。
如何在避免常见陷阱的情况下混合合同同步代码和异步代码?
正如我在异步接口博客文章中所描述的那样, async
是实现细节。 任务返回方法可以同步完成。 但是,我建议避免阻塞实现。 如果由于某种原因无法避免它们,那么我会仔细记录它们的存在。
另外,您可以使用Task.Run
作为实现; 我不建议这样做(原因详细的在我的博客 ),但如果你必须有一个阻挡实现它是一个选项。
希望syncSig运行异步但运行同步的用户呢?
调用线程被同步阻止。 这通常只是UI线程的问题-参见下文。
可以通过异步运行来优化syncSig的用户又如何呢?由于假设他们已经异步运行而不再进行优化,该怎么办?
如果调用代码是ASP.NET,则应直接调用它并await
。 同步代码将被同步执行,这在该平台上是理想的。
如果调用代码是UI应用程序,则需要知道存在同步实现,并且可以将其包装在Task.Run
。 这样可以避免阻塞UI线程,而不管实现是同步还是异步。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.