Cha*_*ice 7 .net c# asynchronous task-parallel-library async-await
我正在修改库以添加异步方法.From 我应该为异步方法公开同步包装器吗?它说我不应该只是Task.Result在调用同步方法时写一个包装器.但是,我如何在异步方法和同步方法之间复制大量代码,因为我们希望在库中保留两个选项?
例如,库当前使用TextReader.Read方法.我们想要使用TextReader.ReadAsync方法的部分异步更改.由于这是库的核心,我似乎需要在同步和异步方法之间复制大量代码(希望尽可能保持代码DRY).或者,我需要重构出来的PreRead,并PostRead似乎杂乱的代码,什么TPL试图修复方法.
我正在考虑将TextReader.Read方法包装在一个Task.Return().即使它是一项任务,TPL的改进也不应该让它切换到不同的线程,我仍然可以使用异步等待大多数代码,就像正常一样.难道然后是确定有同步的包装是公正Task.Result还是Wait()?
我查看了.net库中的其他示例.在StreamReader似乎复制异步与非异步之间的代码.在MemoryStream做了Task.FromResult.
还计划到处都可以添加,ConfigureAwait(false)因为它只是一个库.
更新:
我所说的重复代码是
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)
}
Run Code Online (Sandbox Code Playgroud)
这是一个小例子,但您可以看到唯一的代码更改是等待和任务.
为了说清楚,我想在库中的所有地方使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
除了lib中的同步方法之外,您还想添加异步方法.您链接的文章正好谈到了这一点.它建议为这两个版本创建专门的代码.
现在通常会给出建议,因为:
如果您创建包装器,可能会误导调用者.
现在,它是一个有效的,如果你的后果确定以创建包装左右逢源的策略.它当然可以节省大量代码.但您必须决定是优先同步还是异步版本.另一个效率较低,没有基于性能的理由存在.
你很少在BCL中找到这个,因为实施的质量很高.但是例如ADO.NET 4.5的SqlConnection类使用sync-over-async.执行SQL调用的成本远远高于同步开销.这是一个好的用例.MemoryStream使用(种类)async-over-sync,因为它本质上只是CPU工作,但它必须实现Stream.
实际上是什么开销?预计Task.FromResult每秒可以运行> 1亿,每秒运行数百万,几乎为零Task.Run.与许多事情相比,这是一个很小的开销.
那么,将同步的包装器设置为 Task.Result 或 Wait() 可以吗?
您必须了解异步 IO 的全部内容。它不是关于代码重复,而是关于利用这样一个事实:当工作本质上是异步时,您不需要任何线程。
如果您将同步代码与任务包装在一起,那么您就失去了这一优势。此外,当 API 调用者假设等待的调用会将控制权交还给调用者时,你就会误导他们。
编辑:
你的例子强化了我的观点。不要从使用任务中。同步 api 本身完全没问题,在不需要时不要强制使用 TPL,甚至会导致代码库的行数增加 2 倍。
花点时间正确实现您的异步 api。不要阻塞异步代码,让它一直流到堆栈底部。
| 归档时间: |
|
| 查看次数: |
2320 次 |
| 最近记录: |