我面临着设计执行网络I / O(对于可重用库)的方法的问题。我读过这个问题
c# 5 await/async pattern in API design
以及其他更接近我的问题。
所以,问题是,如果我想提供 async和non-async 方法,我将如何设计这些?
例如,要公开方法的非异步版本,我需要执行类似
的操作public void DoSomething() {
DoSomethingAsync(CancellationToken.None).Wait();
}
我觉得这不是一个很棒的设计。我想建议(例如)如何定义可以包含在公共方法中的私有方法以提供两个版本。
答案 0 :(得分:59)
如果您想要最易维护的选项,只需提供async
API,无需进行任何阻塞调用或使用任何线程池线程即可实现。
如果您真的想同时拥有async
和同步API,那么您将遇到可维护性问题。你真的需要实现它两次:一次async
和一次同步。这两种方法看起来几乎完全相同,因此初始实现很容易,但最终会得到两个几乎完全相同的方法,因此维护是有问题的。
特别是,没有简单的方法来制作async
或同步“包装器”。 Stephen Toub有关于这个主题的最佳信息:
答案 1 :(得分:3)
我同意Marc和Stephen(Cleary)。
(顺便说一下,我开始写这篇评论作为斯蒂芬的回答,但事实证明这篇文章太长了;让我知道是否可以把它作为一个答案写下来,并随意从中取出并以“提供最佳答案”的精神将其添加到斯蒂芬的答案中。)
它真的“依赖”:像Marc所说,重要的是要知道DoSomethingAsync是如何异步的。我们都同意让“sync”方法调用“async”方法并“等待”没有意义:这可以在用户代码中完成。使用单独方法的唯一优势是获得实际的性能提升,以便实现在引擎盖下不同并针对同步方案进行定制。如果“异步”方法正在创建一个线程(或从线程池中获取它),则尤其如此:最终会出现下面使用两个“控制流”的东西,而“承诺”的同步外观将在来电者的背景。这甚至可能会出现并发问题,具体取决于实现方式。
另外在其他情况下,如OP提到的密集型I / O,可能值得拥有两种不同的实现方式。大多数操作系统(肯定是Windows)都有针对两种情况定制的I / O不同机制:例如,异步执行和I / O操作从I / O完成端口等操作系统级别机制中获得了很大的优势,这增加了一些内核中的开销(不重要,但不是空)(毕竟,它们必须进行簿记,调度等),以及更直接的同步操作实现。 代码复杂性也有很大差异,特别是在完成/协调多个操作的函数中。
我要做的是:
这主要是因为两个目标在某种程度上相互对比。如果你想要可维护的代码,显而易见的选择是在异步/等待(或者反过来)方面实现同步(或者更好的是,只提供异步变体并让用户“等待”);如果你想要性能,你应该以不同的方式实现这两个功能,以利用不同的底层机制(来自框架或来自操作系统)。我认为从单元测试的角度来看,如何实际实现API不应该有所不同。
答案 2 :(得分:1)
我遇到了一个同样的问题,但是使用关于异步方法的两个简单事实设法找到了效率和可维护性之间的折衷:
最好在示例中显示:
//Simple synchronous methods that starts third party component, waits for a second and gets result.
public ThirdPartyResult Execute(ThirdPartyOptions options)
{
ThirdPartyComponent.Start(options);
System.Threading.Thread.Sleep(1000);
return ThirdPartyComponent.GetResult();
}
为提供此方法的可维护同步/异步版本,该方法已分为三层:
//Lower level - parts that work differently for sync/async version.
//When isAsync is false there are no await operators and method is running synchronously.
private static async Task Wait(bool isAsync, int milliseconds)
{
if (isAsync)
{
await Task.Delay(milliseconds);
}
else
{
System.Threading.Thread.Sleep(milliseconds);
}
}
//Middle level - the main algorithm.
//When isAsync is false the only awaited method is running synchronously,
//so the whole algorithm is running synchronously.
private async Task<ThirdPartyResult> Execute(bool isAsync, ThirdPartyOptions options)
{
ThirdPartyComponent.Start(options);
await Wait(isAsync, 1000);
return ThirdPartyComponent.GetResult();
}
//Upper level - public synchronous API.
//Internal method runs synchronously and will be already finished when Result property is accessed.
public ThirdPartyResult ExecuteSync(ThirdPartyOptions options)
{
return Execute(false, options).Result;
}
//Upper level - public asynchronous API.
public async Task<ThirdPartyResult> ExecuteAsync(ThirdPartyOptions options)
{
return await Execute(true, options);
}
这里的主要优点是,最有可能更改的中级算法仅实施一次,因此开发人员不必维护两个几乎相同的代码。