假设我有这个示例代码
var t = Float();
...
t.Wait();
async Task Float()
{
while (LoopIsAllowed)
{
TryToComplete();
System.Threading.Thread.Sleep(500);
}
}
在这种情况下,代码同步运行,并且我收到警告“异步方法缺少‘等待’”。我确实需要异步运行 Float() ,因此几乎应该始终考虑此警告。正如我所看到的,这里的问题是,如果我调用
Float()
并稍后等待它,应用程序将无法按预期工作,并且它将卡在 var t = Float()
而不是 t.Wait()
但除了这样之外,我没有看到任何方法可以解决这个问题:
async Task Float()
{
while (LoopIsAllowed)
{
TryToComplete();
await Task.Run(() => System.Threading.Thread.Sleep(500));
}
}
这个修复有任何意义吗?就内存和处理器资源而言,调用
Task.Run
可以吗?或者有更好的方法吗?
正如我所见,修复后调用 var t = Float()
将强制代码同步运行,直到达到 await Task.Run
。然后父代码将继续执行,直到t.Wait()
。只有这样它才会继续迭代 while (LoopIsAllowed)
循环。而这种行为正是我所需要的。
这是正确的吗?
编辑: 如果我的代码完全没有任何延迟,我该怎么办?而且没有其他地方可以等待。我应该添加延迟吗?或者我应该分析代码并在耗时的计算块周围添加 Task.Run 吗?
切勿在异步方法中使用
Thread.Sleep
。
使用
await Task.Delay();
代替 Thread.Sleep
async Task Float()
{
while (LoopIsAllowed)
{
TryToComplete();
await Task.Delay(500);
}
}
并在主方法中使用
GetAwaiter().GetResult()
而不是 wait
var t = Float().GetAwaiter().GetResult();
如果您的目的是引入并行性,则最好在更高级别上使用
ThreadPool
方法将同步工作卸载到 Task.Run
。这意味着通常应该避免将 Task.Run
隐藏在方法链深处。本文解释了推理:我应该为同步方法公开异步包装器吗?在您的情况下,(命名不恰当)Float
方法执行同步工作,因此它应该是具有 void
返回类型的同步方法。然后您可以将其卸载到 ThreadPool
,如下所示:
Task t = Task.Run(() => Float());
//...
t.Wait();
Task.Run
是一个聪明的小方法,它同样可以很好地处理同步和异步委托(具有Task
返回类型的委托)。因此,如果您稍后决定通过将 Float
方法转换为混合同步异步方法来提高其效率(关于线程的利用率),则无需更改调用站点中的任何内容(除了方法名称之外)即可实现此目的现在应该有 Async
后缀):
async Task FloatAsync()
{
while (LoopIsAllowed) // Potential code smell here. Is the LoopIsAllowed volatile?
{
TryToComplete();
await Task.Delay(500);
}
}
Task t = Task.Run(() => FloatAsync());
//...
t.Wait();