【问题标题】:Async-Await thread behavior and questionsAsync-Await 线程行为和问题
【发布时间】:2018-11-28 00:01:16
【问题描述】:

只调用一个内衬异步等待方法的异步等待方法有什么用?它们跨越多少个线程?您最终可以消耗多少 CPU? 嗨,最近我看到很多方法使用 async 和 await 关键字。我仍然无法理解它们的使用和潜在的行为以及它们在如下应用时的优势。当他们将 CPU 用于所有事情时,我也观察到了 CPU 的问题(达到 100%),并且在某些情况下,我发现 thread.run 是 CPU 利用率的罪魁祸首。

myController.cs

[HttpPost]

[ProducesResponseType(body)]

public async Task<IActionResult> Post([FromBody]Body body){

_MyClassService.sendMessage(body);

return Ok(body);

}

我的服务类

Myclass.cs

private async void sendMessage(){

await SendToProperChannel();

}



private async Task SendToProperChannel(){

await DoWorkInProperChannel();

}



private Task<int> DoWorkInProperChannel(){

await SomethingThatTakes5Seconds();

return 1+1;

}

据我所知。

我们使控制器请求异步,以便不阻塞主线程,从而允许在控制器级别接收更高的吞吐量。

我们还将所有函数设为异步,以免阻塞调用这些方法的上层线程。

我也明白,由于我们有这么多的等待,每个人都会返回一个任务,但也有一个线程正在处理等待完成的工作。

我仍然不清楚线程号,但我相信我们会产生超过 1 个线程来满足单个请求。

我的问题。

如果上面的方法没有响应就无法继续,那么让一个衬垫等待有什么用?实际上,API 可以接收更多的吞吐量,但是从那里开始一切都会同步,而且产生更多线程只是为了在我的类中的方法之间跳转似乎并不健康。

如果我只在一个班轮上等待,在这个特定的代码中,结果与删除等待不一样吗?

所有方法也将处理更多吞吐量,因为它们不会阻塞,但也会产生更多线程?

启动异步方法并在同一方法中等待响应的语法是怎样的。

例子:

private async Task<int> myAsyncWorkerMethod(){

var myNeededValue=methodThatINeed();

var mySecondValue=methodWithSecondValue();

await myNeededValue;

await mySecondValue;

return myNeededValue+mySecondValue;

}

与只处理所有下游调用的线程池相比,异步和等待有什么好处,您可以轻松控制其大小并轻松检测线程饥饿和队列深度? (我可能在这里使用了一些可能不适用的 java 术语)

想法

在其他语言中,我已经看到线程上下文更改的影响,如果您的 CPU 不够强大或由于线程池大小而导致线程饥饿,那么这个 .net 不受控制的线程池是否不会产生相同的影响,只是随心所欲地使用 CPU?

如果您需要异步调用返回等待语句的响应,则无需创建异步调用。只有当你需要对上述方法做一些工作时才需要异步?

即使你正在等待另一个类的方法结果,除非你不想阻塞主线程,你也不应该等待它。

提前致谢

【问题讨论】:

  • 没有Thread.sleepThread.Sleep 返回 void ... 所以它不能像await Thread.Sleep(..) 那样使用
  • 是的,你应该等待Task.Delay,而不是Thread.SleepThread.Sleep 言行一致 - 它使线程休眠 N 毫秒,在这段时间内浪费了该线程(这与 await 的好处相反
  • 编辑后...问题出在SomethingThatTakes5Seconds

标签: c# .net multithreading asynchronous async-await


【解决方案1】:

我会尽量一次性回答你的观点和问题:

“我们正在使控制器请求异步,以便不阻塞主线程,从而在控制器级别接收更高的吞吐量。”

不,这完全不正确。打开适当的编译器异常,您会发现使用 async 而不使用 await 是浪费时间,没有任何价值并生成无用的 IL 代码。

“我也明白,由于我们有这么多等待,每个人都会返回一个任务,但还有一个线程正在处理等待完成的工作。”

There is no thread

“我仍然不清楚线程号,但我相信我们正在生成超过 1 个线程来满足单个请求。”

如上所述,这也是无效的。

如果上面的方法没有响应就无法继续,那么让一个衬垫等待有什么用?

基本上没有,代码可能只是:

private async void sendMessage()
{    
    await DoWorkInProperChannel();
}

private Task<int> DoWorkInProperChannel()
{
    // `await Thread.Sleep()` is not valid, `Sleep` returns `void` 
    await Task.Delay(1000);

    return 1+1;
}

注意:以上代码不会创建一个新线程;不是在原始版本中,也不是在我的版本中。

如果我只在一个班轮上等待,在这个特定的代码中,结果与删除等待不一样吗?

完全没有,您需要return 生成的Task 才能使代码相同。当然,async void 方法除外,您无法返回任何内容。

private async Task<int> myAsyncWorkerMethod()
{
    var myNeededValue=methodThatINeed();
    var mySecondValue=methodWithSecondValue();
    await myNeededValue;
    await mySecondValue;

    return myNeededValue+mySecondValue;
}

这称为并行异步代码,与前面的示例无关。是否可以使用并行代码取决于您在做什么。

在其他语言中,我已经看到线程上下文更改的影响,如果您的 CPU 不够强大或由于线程池大小而导致线程不足,那么这个 .net 不受控制的线程池是否不会产生相同的影响,只是随心所欲地使用 CPU?

您发布的代码中没有任何上下文更改,因此不相关。

即使你正在等待另一个类的方法结果,除非你不想阻塞主线程,你也不应该等待它。

您使用的是 ASP.NET Core,没有主线程。

【讨论】:

  • 在第一点上,我表达的不正确,我指的是让它异步并在函数中放置和等待,这样它就不会阻塞我认为这是结束的asp.net线程控制器级别的 async/await 的目标。在同一主题上,那么在同一个堆栈中一遍又一遍地等待大量等待对 CPU 没有影响吗?所以如果我有 10 个等待,它只会使用一个线程,CPU 影响将与删除 async/await 语句相同?
  • @MBG 拥有一长串 aync 方法会产生一些成本,但这仅仅是因为为每个方法分配新状态机的成本(因此主要是对 GC 造成压力)。如果你的 async 方法是单行的,不要使用 await:去掉 async 关键字,直接返回任务。如果这样做,那么由您的方法引起的唯一成本将是方法调用,这可能会被 JIT 编译器内联(因此:完全没有成本)
  • @KevinGosse 如果您省略asyncawait 运行Task,可能会发生奇怪的事情,请参阅here。在这种情况下,最好避免创建方法
  • @CamiloTerevinto 只要它是单行的(所以没有“使用”块、try/catch 或其他任何东西),就不会发生任何奇怪的事情
  • @KevinGosse 我的意思是可以避免只返回其他方法的方法。我现在意识到最后的评论没有意义
猜你喜欢
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-30
  • 2020-12-19
相关资源
最近更新 更多