我正在研究一个可扩展的游戏服务器系统,并试图对这个新项目过度使用await/async。直到现在,我还是遇到了几个陷阱。我已经找出了大部分解决方案,并开发了一种“常见做法”,即如何编写异步方法,以便避免这些缺陷易于检测。不幸的是,下面让我头疼:检测异步/等待滥用:阻止阻止呼叫
1)例:
async Task<int> RunSqlQuery()
{
DbCommand cmd = conn.CreateCommand();
...
cmd.ExecuteReader();
...
}
WHOOOM!由于以下几个原因,这是一个混蛋。一,有一个ExecuteReaderAsync(),它可以解决问题。其次,这个调用将阻止和阻止内部异步框架的工作线程。在桌面环境中不是太关键,但不幸的是,如果您的服务器每秒处理8000个请求,这可能会严重损害您的性能,并且追踪导致问题的确切线条可能非常困难。
2)例:
void ThisHurts()
{
someTask.GetAwaiter().GetResult();
}
async Task<int> RunQuery()
{
...
lock(something)
{
ThisHurts();
}
...
}
我承认这个看起来有点设计,以证明我的观点,但有大量的代码打交道时,这是一个情况是很难避免的。基本上调用等待锁是致命的。它是一个等待发生的灾难(字面上)。有很多解释为什么,相信与否,他们是真实的,建立像上面的东西,你的代码将几乎立即死亡,如果你有你的服务器上的负载和吨的竞争条件开始链接在一起...
所以这个问题是关于如何检测await的那些错误用法。有没有像Resharper这样的工具(它似乎不支持我想要的大部分)。我认为微软应该标记所有非阻塞方法,并强制人们以异步方式包装它们,或者在尝试在异步方法中调用它们时发出编译器错误。此外,他们可能会迫使你使用一个新的“块” - 关键字被添加到所有未标记为非阻塞的方法前面,以便您可以使用它们,但立即显示羞耻。
到目前为止我看到的与异步框架有关的主要问题是,滥用只会在重负载下显示,这通常非常难以调试跟踪,因为它拥有如此多的数据以及附加debuger的问题是走了,因为负荷消失了。真棒;)
'标记的所有无阻塞的方法和力量的人要么将它们包装在异步兼容的方式'这是不可能的。如果你有一个阻塞方法,使其异步的唯一方法是完全重写它以使用非阻塞IO。 – SLaks
这曾经被称为“设计错误”,之前没有听说过它叫做“灾难”。我想,如果你在项目结束时做TDD的话。 –
“追踪导致问题的确切线条非常困难。”我不这么认为。Profiler可以告诉你你的代码在哪里花了很多时间。如果你阻止了很多,你会在那里看到那个方法。 – svick