等待异步链的末尾应该包含繁忙等待/CPU 密集型代码?

问题描述 投票:0回答:2

假设我有这个示例代码

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 吗?

c# asynchronous async-await
2个回答
1
投票

切勿在异步方法中使用

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();

1
投票

如果您的目的是引入并行性,则最好在更高级别上使用

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();
© www.soinside.com 2019 - 2024. All rights reserved.