代码之家  ›  专栏  ›  技术社区  ›  Z.R.T.

异步等待方法在调用控制器操作方法时无法并行工作

  •  -1
  • Z.R.T.  · 技术社区  · 7 年前

    article . 但是出了点问题,我得到的不是5秒而是12秒。为什么,是什么原因?

    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View();
        }
    
        [HttpGet]
        public async Task<string> IndexAsync()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            await Delay(3);
            await Delay(5);
            await Delay(4);
            stopwatch.Stop();
            return stopwatch.Elapsed.Seconds.ToString();
        }
    
    
        private async Task<Int32> Delay(int sec)
        {
            await Task.Delay(1000 * sec);
            return sec;
        }
    
    }
    

    结果是:

    enter image description here

    3 回复  |  直到 7 年前
        1
  •  3
  •   Rawling isekaijin    7 年前

    在文章中,代码 启动所有三个任务 然后 :

    var contentTask = service.GetContentAsync(); // start task 1
    var countTask = service.GetCountAsync();
    var nameTask = service.GetNameAsync();
    
    var content = await contentTask; // wait until task 1 is finished
    var count = await countTask;
    var name = await nameTask;
    

    你的代码 :

    await Delay(3); // start task 1 and wait until it is finished
    await Delay(5);
    await Delay(4);
    
        2
  •  4
  •   Damien_The_Unbeliever    7 年前

    很多人脑子里都有一个坏模型 await 做。尽管它叫什么名字,他们还是开始相信 开始 事情正在发生。没有什么比事实更离谱了 1个 .

    等待 右手边有些表情。它不关心这个表达式是什么,它如何工作,等等。它只关心这个表达式将产生 可期待的 2个 . 到目前为止,你会遇到的最常见的等待 Task Task<T> .

    然后 做它简单的工作-这是值得期待的事情吗 完成 ? 如果是,我们会得到结果(如果有的话),然后继续。 ,那么我们就不能再取得任何进展了。如果有人能同时利用好我们的线索,我们就让它发生。当等待的事情结束时,我们已经安排 简历 执行包含 .


    等待 不在乎 为什么? async 改变你的 Delay 方法。但是 等待 异步 (这是一个 任务 -一个已经在跑了。


    1个 async ≅ parallelism ≅ using threads . 这从来不是真的,但这是我们一直在努力纠正的。所以他们认为 异步 一定是说我们在创建线程”

    Rawling 表达式 引用以前通过调用 任务 -返回方法。这就是你自己的代码与众不同的原因。

        3
  •  0
  •   Mrinal Kamboj    7 年前

    var result = await Task.WhenAll(Delay(3),Delay(4),Delay(5));
    

    Task.WhenAll 将提供代表 Task ,当提供的集合中的所有任务都已完成执行时(成功/失败)。所以我们只是 await 代表 task 并得到必要的行为,尽管它仍然不能保证5秒的预期。 result 会是一个 int[] ,它将包含每个调用延迟方法的值。

    在你的情况下 Delay 方法负责开始任务,使用 await Task.Delay(1000 * sec); ,因此,如果您只调用 延迟 如接受的答案所示,然后分别 等待 ,然后它们仍然并行执行,但假设您不确定,或者延迟方法如下:

    private Task Delay(int sec)
    {
      return Task.Delay(1000 * sec);
    }
    

    await Task.WhenAll(...) 变得很重要,因为它将启动未启动的任务,否则将只是等待它们完成。否则,使用接受的答案,您将不会收到好处。


    更多关于 等待 作为一般信息,来自C#6.0规范指南:

    等待表达式的任务必须是可等待的。如果满足以下条件之一,则表达式t是可等待的:

    • t是编译时类型dynamic
    • GetAwaiter 不带参数和类型参数,返回类型a,其中包含以下所有内容:

      • System.Runtime.CompilerServices.INotifyCompletion (以下简称 INotifyCompletion 为了简洁)
      • 有一个类型为bool的可访问、可读的实例属性IsCompleted
      • A有一个不带参数和类型参数的可访问实例方法GetResult。

    等待者 方法是获取任务的等待者。类型A被称为等待表达式的等待者类型。

    目的 IsCompleted 属性确定任务是否已完成。如果是,则无需暂停评估。

    INotifyCompletion.OnCompleted 方法是注册任务的“continuation”;即委托(类型为 System.Action

    GetResult方法的目的是在任务完成后获取其结果。


    等待表达式的分类

    await t 与表达式的分类方式相同 (t).GetAwaiter().GetResult() . 因此,如果返回类型为 GetResult is void ,和 await_expression 被归为零。如果它有 non-void return type T ,和 被归类为类型的值 T .


    await表达式的运行时计算

    • 通过计算表达式获得等待者a (t).GetAwaiter()
    • bool b是通过计算表达式(A)得到的
    • System.Runtime.CompilerServices.ICriticalNotifyCompletion (以下简称 ICriticalNotifyCompletion 为了简洁)。此检查在绑定时完成;即在运行时(如果a具有动态编译时类型),否则在编译时进行。让r表示恢复委托(迭代器):

      • 如果a不执行 ICriticalNotifyCompletion公司 (a as (INotifyCompletion)).OnCompleted(r) 被评估。
      • 如果a确实实现了ICriticalNotifyCompletion,则 (a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r) 被评估。
      • 然后将挂起计算,并将控制权返回给异步函数的当前调用方。
    • (if b was true ) ,或在稍后调用恢复委托时 (if b was false ) ,表达式 (a).GetResult() 被评估。如果它返回一个值,则该值是

    • 接口方法的等待者实现 inotificompletion.OnCompleted完成 ICriticalNotifyCompletion.UnsafeOnCompleted 应该使委托r最多被调用一次。否则,封闭异步函数的行为是未定义的。