有很多问题要问是否混合使用异步和同步代码。
大多数答案都说,为异步方法公开同步包装器,并为同步方法公开异步包装器是个坏主意。
但是,没有一个答案解决了您必须混合异步和同步代码的具体方案,以及如何避免因此而产生的常见陷阱。
请参阅以下示例:
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,但不会更长,因为他们认为它已经运行异步?
答案 0 :(得分:3)
这是混合异步和同步的正确方法吗?
大多数情况下是的。如果你有一个通用接口,其中一些实现将能够提供同步实现,但其他实现只能是异步实现,返回Task<T>
是有意义的。一个简单的return Task.FromResult
可能是合适的。但也有例外,见下文。
希望syncSig运行异步的用户怎么样,而是运行同步?
接口的某些方面无法用代码表示。
如果IContract
要求它在返回Task
之前不会阻塞调用线程超过X毫秒(这个形式不是一个合理的硬性要求,但是你得到了基本的想法,并且它无论如何都会阻止线程,这违反了合同,用户应该将其作为一个错误报告给实施syncSig的人。
如果您的IContract
要求它不会抛出同步异常,那么使用Task.FromException
或等价物报告任何错误,并且您的syncSig无论如何都会抛出同步异常,那也是合同冲突。
除此之外,如果syncSig只是立即返回正确的结果,那么没有理由为什么这会打扰任何用户。
如果用户可以通过运行异步来优化syncSig,但不会更长,因为他们认为它已经运行异步?
如果同步运行的syncSig不会导致任何问题,那么无关紧要,它还不需要进行优化。
如果同步运行的syncSig确实会导致问题,那么基本的调试工具应该很快告诉开发人员syncSig导致问题并且应该进行调查。
答案 1 :(得分:1)
但是,没有一个答案解决了您必须混合异步和同步代码的具体方案,以及如何避免因此而产生的常见陷阱。
那是因为这是一个坏主意。任何混合代码都应该是完全临时的,仅在转换到异步API时才存在。
那就是说,我写了an entire article on the subject。
如何在避免常见陷阱场景的同时混合合同同步和异步代码?
正如我在blog post on async interfaces中所描述的那样,async
是一个实现细节。任务返回方法可以同步完成。但是,我建议避免阻止实施;如果由于某种原因他们无法避免,那么我会仔细记录它们的存在。
或者,您可以使用Task.Run
作为实现;我不建议这样做(原因为detailed on my blog),但如果您 有阻止实施,则可以选择此选项。
希望syncSig运行异步的用户怎么样,而是运行同步?
同步阻止调用线程。这通常只是UI线程的一个问题 - 见下文。
如果用户可以通过运行异步来优化syncSig,但不会更长,因为他们认为它已经运行异步?
如果调用代码是ASP.NET,那么它应该直接调用它await
。同步代码将同步执行,这在该平台上是可取的。
如果调用代码是UI应用程序,那么它需要知道存在同步实现,并且可以将其调用包含在Task.Run
中。这可以避免阻塞UI线程,无论实现是同步还是异步。