Mar*_*rko 16 .net c# asynchronous
我正在开发一个需要同时支持同一逻辑/方法的异步和同步版本的项目。所以例如我需要有:
public class Foo
{
public bool IsIt()
{
using (var conn = new SqlConnection(DB.ConnString))
{
return conn.Query<bool>("SELECT IsIt FROM SomeTable");
}
}
public async Task<bool> IsItAsync()
{
using (var conn = new SqlConnection(DB.ConnString))
{
return await conn.QueryAsync<bool>("SELECT IsIt FROM SomeTable");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这些方法的异步和同步逻辑在各方面都是相同的,除了一个是异步的,另一个不是。在这种情况下,是否有合法的方法来避免违反 DRY 原则?我看到有人说你可以在异步方法上使用 GetAwaiter().GetResult() 并从你的同步方法调用它?该线程在所有情况下都安全吗?有没有另一种更好的方法来做到这一点,还是我被迫复制逻辑?
Eri*_*ert 15
你在你的问题中问了几个问题。我会将它们分解为与您略有不同的方法。但首先让我直接回答这个问题。
我们都想要一款轻便、优质、便宜的相机,但俗话说,三者中最多只能得到两台。你在这里也处于同样的情况。您需要一个高效、安全且在同步和异步路径之间共享代码的解决方案。你只会得到其中的两个。
让我分解一下原因。我们将从这个问题开始:
我看到有人说您可以
GetAwaiter().GetResult()在异步方法上使用并从同步方法中调用它?该线程在所有情况下都安全吗?
这个问题的重点是“我可以通过让同步路径简单地对异步版本进行同步等待来共享同步和异步路径吗?”
在这一点上让我非常清楚,因为它很重要:
您应该立即停止接受这些人的任何建议。
这是非常糟糕的建议。从异步任务同步获取结果是非常危险的,除非您有证据表明该任务已正常或异常完成。
这是非常糟糕的建议的原因是,考虑一下这种情况。您想修剪草坪,但您的割草机刀片坏了。您决定遵循以下工作流程:
发生什么了?你永远睡不着,因为检查邮件的操作现在被限制在邮件到达之后发生的事情上。
这是非常容易陷入这种情况,当你同步等待上的任意任务。该任务可能在现在正在等待的线程的未来安排了工作,而现在该未来将永远不会到达,因为您正在等待它。
如果您进行异步等待,那么一切都很好!你会定期检查邮件,在等待的时候,你会做一个三明治或做你的税或其他任何事情;你在等待时不断完成工作。
永远不要同步等待。如果任务完成了,就没有必要了。如果任务没有完成但被安排在当前线程之外运行,则效率低下,因为当前线程可能正在为其他工作提供服务而不是等待。如果任务没有完成并且调度在当前线程上运行,则挂起同步等待。没有充分的理由再次同步等待,除非您已经知道任务已完成。
有关此主题的进一步阅读,请参阅
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
斯蒂芬比我能更好地解释现实世界的场景。
现在让我们考虑“另一个方向”。我们可以通过让异步版本简单地在工作线程上执行同步版本来共享代码吗?
由于以下原因,这可能并且确实可能是一个坏主意。
如果同步操作是高延迟的IO工作,效率很低。这本质上是雇佣一个工人并让该工人睡觉直到任务完成。线程非常昂贵。默认情况下,它们最少消耗一百万字节的地址空间,它们需要时间,它们需要操作系统资源;你不想烧掉一个做无用工作的线程。
同步操作可能不会被写入线程安全。
如果高延迟工作受处理器限制,这是一种更合理的技术,但如果是,那么您可能不想简单地将其交给工作线程。您可能想要使用任务并行库将其并行化到尽可能多的 CPU,您可能想要取消逻辑,并且您不能简单地让同步版本完成所有这些,因为那样它就已经是异步版本了。
进一步阅读;再次,斯蒂芬解释得很清楚:
为什么不使用 Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Task.Run 的更多“做和不做”场景:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
那会给我们留下什么?这两种共享代码的技术要么导致死锁,要么导致效率低下。我们得出的结论是你必须做出选择。您想要一个高效、正确且让调用者满意的程序,还是想要通过在同步和异步路径之间复制少量代码来节省一些击键次数?恐怕你两者都得不到。
很难对这个问题给出一个千篇一律的答案。不幸的是,没有一种简单、完美的方法可以在异步和同步代码之间实现重用。但这里有一些原则需要考虑:
Query()一种方法和QueryAsync()另一种方法),或者使用不同的设置建立连接。因此,即使它在结构上相似,通常也有足够的行为差异,值得将它们视为具有不同要求的单独代码。注意File 类中方法的Async和Sync实现之间的差异,例如:没有努力使它们使用相同的代码Task.FromResult(...)。祝你好运。
| 归档时间: |
|
| 查看次数: |
565 次 |
| 最近记录: |