Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步等待方法在调用控制器操作方法时不能并行工作_C#_Asp.net Mvc_Async Await - Fatal编程技术网

C# 异步等待方法在调用控制器操作方法时不能并行工作

C# 异步等待方法在调用控制器操作方法时不能并行工作,c#,asp.net-mvc,async-await,C#,Asp.net Mvc,Async Await,我有一个简单的控制器,它有两种方法,分别是Index和IndexAsync同步和异步。我试着做和这篇文章中写的一样的事情。但是出了点问题,结果不是5秒,而是12秒。为什么,原因是什么 public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); } [HttpGet] public async Ta

我有一个简单的控制器,它有两种方法,分别是Index和IndexAsync同步和异步。我试着做和这篇文章中写的一样的事情。但是出了点问题,结果不是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;
    }

}
结果是:


进行以下代码修改,以使异步调用能够一起执行,而不是一个接一个地执行:

var result = await Task.WhenAll(Delay(3),Delay(4),Delay(5));
Task.WhenAll将提供代表性任务,该任务将在提供的集合中的所有任务完成时完成执行成功/失败。因此,我们只是等待代表性任务,并获得必要的行为,尽管它仍然可能无法保证预期的5秒。结果将是一个int[],它将包含每个Delay方法调用的值

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

private Task Delay(int sec)
{
  return Task.Delay(1000 * sec);
}
然后使用等待任务。当。。。变得很重要,因为它将启动未启动的任务,否则将只等待它们完成。否则,使用已接受的答案,您将无法获得福利

更多关于Wait的一般信息,请参见C 6.0规范指南:

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

t是编译时类型的动态 t有一个名为GetAwaiter的可访问实例或扩展方法,该方法没有参数和类型参数,并且返回类型a,以下所有参数均适用:

A实现接口System.Runtime.CompilerServices.INotifyCompletion(以下称为 INotifyCompletion为简洁起见 具有类型为bool的可访问、可读的实例属性IsCompleted 具有无参数和无类型参数的可访问实例方法GetResult。 GetAwaiter方法的目的是获取任务的等待器。类型A被称为等待表达式的等待器类型

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

INotifyCompletion.OnCompleted方法的目的是注册任务的继续;i、 e.任务完成后将调用的System.Action类型的委托

GetResult方法的目的是在任务完成后获得任务的结果

此结果可能是成功完成的,可能带有结果值,也可能是GetResult方法引发的异常

等待表达式的分类 表达式await t的分类方式与表达式t.GetAwaiter.GetResult的分类方式相同。因此,如果GetResult的返回类型为void,那么wait_表达式被分类为nothing。如果它具有非void返回类型T,那么await_表达式被分类为类型T的值

等待表达式的运行时计算 在运行时,表达式wait t t的计算如下:

private Task Delay(int sec)
{
  return Task.Delay(1000 * sec);
}
通过计算表达式t.GetAwaiter获得等待器a 布尔b是通过计算表达式A得到的 如果b为false,则评估取决于a是否实现接口 System.Runtime.CompilerServices.ICriticalNotifyCompletion为简洁起见,以下称为ICriticalNotifyCompletion。该检查在绑定时进行;i、 e.在运行时,如果a具有动态编译时类型,则在编译时,否则。让r表示恢复委托迭代器:

如果未实现ICriticalNotifyCompletion,则表达式 将评估as INotifyCompletion.OnCompleted。 如果实现了ICriticalNotifyCompletion,则表达式 将评估as ICriticalNotifyCompletion.UnsafeonCompleted。 然后暂停求值,并将控制返回给异步函数的当前调用方。 如果b为true,则在随后调用恢复委托(如果b为false)时立即计算表达式a.GetResult。如果它返回一个值,则该值是 等待你的表情。否则,结果将一事无成

等待者对接口方法INotifyCompletion.OnCompleted和ICriticalNotifyCompletion.UnsafeOnCompleted的实现应导致委托r最多被调用一次。否则,封闭异步函数的行为是未定义的

在本文中,代码启动所有三项任务,然后等待所有三项任务:

您的代码依次启动和等待每个任务:


许多人在他们的头脑中有一个糟糕的模型等待做什么。尽管它叫什么名字,但他们不知何故开始相信它是事情发生的开始。没有比这更离谱的了。

等待在它的右边有一些表达。它不关心这个表达式是什么,它是如何工作的,等等。它所关心的是这个表达式将产生一些可期待的东西。到目前为止,您将遇到的最常见的等待对象是任务和任务

然后等待完成它的简单工作——等待的事情完成了吗?如果是这样,我们将得到结果,如果有的话,然后继续。如果没有,那么我们就无法取得任何进一步的进展。如果其他人能同时很好地利用我们的线程,我们会让它发生。当等待完成时,我们已经安排好恢复包含等待的方法的执行

我只想重申——等待并不关心等待是如何或为什么产生的。这是其他人的工作——在这里,这是由改变延迟方法的异步机器完成的。但是await并不关心该方法是否标记为async,这是该方法的一个实现细节,而不是其签名的一部分,不管它出现在哪里,只是它承诺返回一个热任务——一个已经运行的任务

1我认为这主要是因为有些人已经学会了异步近似等式≅ 相似≅ 使用线程。这从来都不是真的,但这是我们一直在努力纠正的。所以他们认为异步一定意味着我们正在创建线程


2Indeed,在您链接到的文章中,表达式已经解决的问题提到了以前通过调用任务返回方法初始化的变量。这就是您自己的代码与众不同的原因。

当您使用wait一个接一个地进行异步调用时,必然会发生这种情况,这将使3+4+5=12秒的结果成为您需要的任务。当,请注意,您链接到的代码分别调用了包含延迟的方法,然后等待结果。@Damien_,我试图遵循本文中使用的方法。您能解释一下,我的代码和文章代码之间的区别吗?var contentTask=service.GetContentAsync;-调用GetContentAsync并在进行过程中返回。然后通过调用其他异步方法启动另外两个异步操作,只有在这一点之后,它们才会编写wait contentTask。您可以解释一下,我的代码和您的代码有什么区别吗?在您的代码中,它是同步的,因为它将在每个等待语句中等待,只是由于等待,它不会阻止调用方,而是Task.whall将一起执行为什么要将任务插入列表中?简单的var result=wait Task.WhenAllDelay3,Delay4,Delay5;!不管用哪种方法都可以,没有什么区别,我想在explanation@ChayimFriedman做了您建议的更改,使代码更清晰谢谢,这是关于等待操作员的非常有价值的评论
await Delay(3); // start task 1 and wait until it is finished
await Delay(5);
await Delay(4);