RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 947503
Accepted
A1essandro
A1essandro
Asked:2020-02-21 16:58:08 +0000 UTC2020-02-21 16:58:08 +0000 UTC 2020-02-21 16:58:08 +0000 UTC

Task.Run - 异步/等待反模式?C#

  • 772

最近我读了一篇关于 Habré 的文章(更新:我从评论中意识到我需要附上一个引用,关于这个问题是进一步的)

一旦代码到达 Task.Run() 方法,就会从线程池中取出另一个线程,并在其中执行我们传递给 Task.Run() 的代码。旧线程,作为一个体面的线程,返回到池中并等待再次被调用以完成工作。新线程执行传递的代码,到达同步操作,同步执行(等待操作完成),然后继续执行代码。换句话说,操作一直保持同步:我们和以前一样,在同步操作执行期间使用线程。唯一的区别是我们在调用 Task.Run() 和返回 ExecuteOperation() 时花费了时间切换上下文。一切都变得更糟了。

那里讨论的问题之一是调用Task.Run是一种反模式,它仅用于 GUI 响应性。

问题在于它在Task.Run(() => _anyWork())哪里_anyWork()包含同步代码。如果你这样做,文章中写的内容听起来很合乎逻辑:

await DoWork();
...

Task DoWork() => Task.Run(_work);

是的,在这种情况下,会在线程池上创建额外的负载。但是,如果你这样做:

var task1 = DoWork1();
var task2 = DoWork2();
var task3 = DoWork3();
await Task.WhenAll(task1, task2, task3);

Task.Run立即变成普通代码,对吗?

将执行此代码的线程将创建三个其他线程(upd : speculative: 启动将工作添加到要运行的队列中ThreadPool),它们将并行执行它们的工作。此外,当他遇到 时await,他将返回控制权(最终,他很可能会返回池中)。如果不是请更正。

如果是这样,问题就来了:这条线在哪里,在一个糟糕的实现和一个正常的实现之间?在非库代码中,很明显 - 如果调用了一个方法,并且等待在更远的地方,那么您可以执行Task.Run. 一方面,在库中,如果客户端在调用后等待它们,我们可以并行化方法的工作。另一方面,如果客户端在调用时期望立即得到结果,我们可以徒劳地增加负载。我们无法确切知道客户端将如何调用这些方法,我们只能在文档中给出建议。

也许有一些来自 MS 的官方建议?在 msdn 上,我只找到了对这些方法如何工作的干巴巴的描述。

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    tym32167
    2020-02-21T19:14:34Z2020-02-21T19:14:34Z

    那里讨论的问题之一是调用 Task.Run 是一种反模式,仅用于 GUI 响应性。

    任务运行方法

    将给定的工作排入队列以在 ThreadPool 中运行,并返回该工作的任务或任务句柄。

    也就是说,Task.Run 是一种在线程池中执行某些工作的方法,能够异步等待结果。

    什么时候可能需要它?可能的用例:

    1) 启动 IO/CPU 加载池线程,以免加载主 UI 线程(示例)

    2) 从 UI 线程同步运行异步代码。当您有一个大型应用程序并且您逐渐切换到异步调用而不是同步调用时,这可能是必需的,但并非在任何地方您仍然可以异步调用您的新 API,但是您需要避免 UI 线程中的死锁,例如Task.Run(()=>CallSmthgAsync().GetAwaiter().GetResult()).GetAwaiter().GetResult();- 这个不是很漂亮的代码,应该修好了,但并不总是可以一次性实现异步调用,所以我自己做了,有时也看到了。

    何时不要:当您不关心当前线程被某些同步工作阻塞的事实时。

    在问题的示例中,任务已经在线程池中运行,因此切换到另一个线程并在那里执行同步操作是没有意义的。但是如果从 UI 线程启动相同的工作,那么如果没有Task.Run整个 UI,应用程序就会崩溃。

    至于图书馆代码。根据需求和用例构建您的 API 及其实现。如果您有很多 I/O 操作(使用文件、使用网络、使用数据库),那么这本身就暗示需要异步 API。如果您对选择同步 API 和异步 API 感到困惑,那么请同时实现这两种 API,让客户端选择他需要的。

    因此得出结论:最重要的是,为了不破坏柴火,始终知道自己在做什么以及为什么要这样做。当你有理由这样做时切换上下文。如果你的每一行代码都是合理的,并且有理由这样写,那么你在选择好代码/坏代码方面的问题就会少得多。

    • 4
  2. vitidev
    2020-02-21T18:55:30Z2020-02-21T18:55:30Z

    *Async 方法代码在当前线程上执行,直到第一个 await。并且当前线程将被捕获。比如HttpClient在开始异步请求之前同步请求dns,这可能是一个很长的操作。

    而且您可以决定更担心的是什么 - 等待当前线程或 Task.Run() 的开销(实际上这是最小的,除了臃肿的代码)

    • 1
  3. dgzargo
    2020-02-21T17:11:14Z2020-02-21T17:11:14Z

    使用Task:

    • 如果在里面你需要使用异步操作:
      • 里面有几个
      • 同步代码和异步操作
      • 这是一篇关于using(IDisposable)潜在错误的有趣文章
    • 如果您正在执行自己的异步操作(例如,将事件转换为Task) - 经常使用TaskCompletionSource

    第二个看起来像一个I/O-bound操作 - 我不确定是否将它写为一个单独的项目

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5