参见英文答案 >
Using async await when implementing a library with both synchronous and asynchronous API for the same functionality2个
我正在修改库以添加异步方法.从 Should I expose synchronous wrappers for asynchronous methods?开始,它指出我不应该在调用同步方法时在Task.Result周围编写一个包装器.但是,我如何在异步方法和同步方法之间复制大量代码,因为我们希望在库中保留两个选项? @H_301_3@例如,库当前使用TextReader.Read方法.部分异步更改我们想使用TextReader.ReadAsync方法.由于这是库的核心,我似乎需要在同步和异步方法之间复制大量代码(希望尽可能保持代码DRY).或者我需要在PreRead和PostRead方法中重构它们,这似乎使代码混乱,以及TPL试图解决的问题. @H_301_3@我正在考虑将TextReader.Read方法包装在Task.Return()中.即使它是一项任务,TPL的改进也不应该让它切换到不同的线程,我仍然可以使用异步等待大多数代码,就像正常一样.那么同步的包装器可以只是Task.Result还是Wait()呢? @H_301_3@我查看了.net库中的其他示例. StreamReader似乎复制了异步和非异步之间的代码. MemoryStream执行Task.FromResult. @H_301_3@还计划到处都可以添加ConfigureAwait(false),因为它只是一个库. @H_301_3@更新: @H_301_3@我所说的重复代码是
我正在修改库以添加异步方法.从 Should I expose synchronous wrappers for asynchronous methods?开始,它指出我不应该在调用同步方法时在Task.Result周围编写一个包装器.但是,我如何在异步方法和同步方法之间复制大量代码,因为我们希望在库中保留两个选项? @H_301_3@例如,库当前使用TextReader.Read方法.部分异步更改我们想使用TextReader.ReadAsync方法.由于这是库的核心,我似乎需要在同步和异步方法之间复制大量代码(希望尽可能保持代码DRY).或者我需要在PreRead和PostRead方法中重构它们,这似乎使代码混乱,以及TPL试图解决的问题. @H_301_3@我正在考虑将TextReader.Read方法包装在Task.Return()中.即使它是一项任务,TPL的改进也不应该让它切换到不同的线程,我仍然可以使用异步等待大多数代码,就像正常一样.那么同步的包装器可以只是Task.Result还是Wait()呢? @H_301_3@我查看了.net库中的其他示例. StreamReader似乎复制了异步和非异步之间的代码. MemoryStream执行Task.FromResult. @H_301_3@还计划到处都可以添加ConfigureAwait(false),因为它只是一个库. @H_301_3@更新: @H_301_3@我所说的重复代码是
public decimal ReadDecimal() { do { if (!Read()) { SetInternalProperies() } else { return _reader.AsDecimal(); } } while (_reader.hasValue) } public async Task<decimal> ReadDecimalAsync() { do { if (!await ReadAsync()) { SetInternalProperies() } else { return _reader.AsDecimal(); } } while (_reader.hasValue) }@H_301_3@这是一个小例子,但您可以看到唯一的代码更改是等待和任务. @H_301_3@为了说清楚我想在库中的所有位置使用async / await和TPL进行编码,但我仍然需要使用旧的同步方法.我不仅仅是Task.FromResult()的同步方法.我想的是有一个标志,说我想要同步方法,并在根检查标志的东西
public decimal ReadDecimal() { return ReadDecimalAsyncInternal(true).Result; } public async Task<decimal> ReadDecimal() { return await ReadDecimalAsyncInternal(false); } private async Task<decimal> ReadDecimalAsyncInternal(bool syncRequest) { do { if (!await ReadAsync(syncRequest)) { SetInternalProperies() } else { return _reader.AsDecimal(); } } while (_reader.hasValue) } private Task<bool> ReadAsync(bool syncRequest) { if(syncRequest) { return Task.FromResult(streamReader.Read()) } else { return StreamReader.ReadAsync(); } }
解决方法
除了lib中的同步方法之外,您还想添加异步方法.您链接的文章正好谈到了这一点.它建议为这两个版本创建专门的代码.
@H_301_3@现在通常会给出建议,因为:
@H_301_3@>异步方法应该是低延迟.为了提高效率,他们应该在内部使用异步IO.
>出于效率原因,同步方法应在内部使用同步IO. @H_301_3@如果您创建包装器,可能会误导调用者. @H_301_3@现在,如果你对后果感到满意,那么就可以通过两种方式创建包装器.它当然可以节省大量代码.但您必须决定是优先同步还是异步版本.另一个效率较低,没有基于性能的理由存在. @H_301_3@你很少在BCL中找到这个,因为实施的质量很高.但是例如ADO.NET 4.5的sqlConnection类使用sync-over-async.执行sql调用的成本远远高于同步开销.这是一个好的用例. MemoryStream使用(种类)async-over-sync,因为它本质上只是cpu工作,但它必须实现Stream. @H_301_3@实际上是什么开销?期望能够每秒运行> 1亿个Task.FromResult,并且每秒运行数百万个几乎为零的Task.Run.与许多事情相比,这是一个很小的开销.
>出于效率原因,同步方法应在内部使用同步IO. @H_301_3@如果您创建包装器,可能会误导调用者. @H_301_3@现在,如果你对后果感到满意,那么就可以通过两种方式创建包装器.它当然可以节省大量代码.但您必须决定是优先同步还是异步版本.另一个效率较低,没有基于性能的理由存在. @H_301_3@你很少在BCL中找到这个,因为实施的质量很高.但是例如ADO.NET 4.5的sqlConnection类使用sync-over-async.执行sql调用的成本远远高于同步开销.这是一个好的用例. MemoryStream使用(种类)async-over-sync,因为它本质上只是cpu工作,但它必须实现Stream. @H_301_3@实际上是什么开销?期望能够每秒运行> 1亿个Task.FromResult,并且每秒运行数百万个几乎为零的Task.Run.与许多事情相比,这是一个很小的开销.