C# 为什么异步控制器方法像同步方法一样执行?
我创建了一个示例.NET核心WebApi应用程序来测试异步方法如何提高吞吐量。应用程序托管在IIS 10上 以下是我的控制器的代码:C# 为什么异步控制器方法像同步方法一样执行?,c#,async-await,asp.net-core-webapi,C#,Async Await,Asp.net Core Webapi,我创建了一个示例.NET核心WebApi应用程序来测试异步方法如何提高吞吐量。应用程序托管在IIS 10上 以下是我的控制器的代码: [HttpGet("sync")] public IEnumerable<string> Get() { return this.GetValues().Result; } [HttpGet("async")] public async Task<IEnumerable<string>> GetAsync() {
[HttpGet("sync")]
public IEnumerable<string> Get()
{
return this.GetValues().Result;
}
[HttpGet("async")]
public async Task<IEnumerable<string>> GetAsync()
{
return await this.GetValues();
}
[HttpGet("isresponding")]
public Task<bool> IsResponding()
{
return Task.FromResult(true);
}
private async Task<IEnumerable<string>> GetValues()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
return new string[] { "value1", "value2" };
}
其中,{apiMethod}
是“同步”还是“异步”,取决于用户输入
在这两种情况下,服务器长时间(约40秒)不响应。
我建议在异步情况下,服务器应该继续快速地为请求提供服务,但事实并非如此
更新1:
我更改了如下客户端代码:
Parallel.For(0, 10000, ((i, state) =>
{
var httpClient = new HttpClient();
httpClient.GetAsync($"http://localhost:50001/api/values/{apiMethod}");
}));
using (var httpClient = new HttpClient())
{
var sw = Stopwatch.StartNew();
// this method should evaluate fast when we called async version and should evaluate slowly when we called sync method (due to busy threads ThreadPool)
var isAlive = httpClient.GetAsync($"http://localhost:50001/api/values/isresponding").Result.Content;
Console.WriteLine($"{sw.Elapsed.TotalSeconds} sec.");
}
调用IsResponding()
方法执行了很长时间
更新2
是的,我知道异步方法是如何工作的。是的,我知道如何使用HttpClient。这只是证明理论的一个例子
更新3
正如StuartLC在其中一条评论中提到的那样,IIS以某种方式发送或阻止请求。当我以自托管方式启动WebApi时,它开始按预期工作:
您似乎不理解异步。它不会使响应返回得更快。在操作执行的所有操作完成之前,无论是否异步,都无法返回响应。如果说有什么区别的话,异步实际上是比较慢的,即使只是稍微慢一点,因为异步处理中涉及的额外开销不是同步处理所必需的 async所做的是潜在地允许为请求提供服务的活动线程返回池以服务其他请求。换句话说,异步是关于规模,而不是性能。只有当您的服务器被大量请求阻塞时,您才会看到好处。然后,当传入的请求通常已排队进行同步时,您将处理来自某些异步任务的附加请求,这些任务的线程因此而失效。此外,根本不能保证线程将被释放。如果异步任务立即或接近立即完成,线程将被保留,就像同步一样 编辑 您还应该意识到IIS Express是单线程的。因此,对于性能调优来说,这不是一种好的语言。如果同时运行1000个请求,999个请求将立即排队。然后,您没有执行任何异步工作—只是返回一个已完成的任务。因此,线程永远不会被释放,因此在这种情况下,sync和async之间没有任何区别。因此,您只需要处理999个请求队列所需的时间(加上最后的状态检查)。如果您采取以下措施,您可能会更幸运地挑出差异:
await Task.Delay(500);
而不仅仅是返回
Task.FromResult
。这样,线程上的实际空闲时间可能允许它返回池。我不确定这是否会产生任何重大改进,但您应该在服务器中的每个可等待线程上调用ConfigureAwait(false)
,包括在GetAsync
中
它应该能更好地利用线程。IIS以某种方式发送或阻止请求(如其中一条评论中所述)。当我以自托管方式启动WebApi时,它开始按预期工作:
isresponsible
方法在向异步方法发出大量请求之后的执行时间非常快,大约为0.02秒。
在大量请求同步方法之后,
isresponsible
方法的执行时间非常慢,大约为35秒。httpClient.GetAsync(url)。结果
ASP.NET核心没有同步上下文…获取了此链接以获取大多数信息…问题可能出在客户端?尝试使用System.Net.ServicePointManager.DefaultConnectionLimit=100;System.Net.ServicePointManager.Expect100Continue=false;System.Net.ServicePointManager.usenaglalgorithm=true在发出请求之前,在客户端执行code>操作。可能重复@Archer,这正是我想要的-等待服务器响应并测量响应时间。首先,我使用我的服务器请求长时间运行的方法,然后检查它将如何服务于其他请求。没有必要这样做,因为ASP.NET Core中没有SyncContext对不起,也许我的问题不是很清楚。请看一下我的代码中的操作顺序(在我的理解中):1。对“异步”方法执行大量请求。所有请求都开始执行作业,直到Task.Delay()。然后,它们中的每一个都应该快速地解除其线程的阻塞(因为等待)。2.对“isresponsible”方法执行同步请求以测量其执行速度。在步骤1中没有阻塞请求。为什么来自步骤2的请求(只有一个是同步的)执行得这么长?如果执行步骤1的请求的所有线程都返回到池中,那么来自步骤2的请求应该执行得很快,因为ThreadPool中有空闲池。正如我所说,IIS Express是单线程的。这很可能是您在这里遇到的问题,特别是当您在Visual Studio之外运行应用程序时,它工作正常。Chris,为什么您决定让我在IIS Express中运行我的应用程序?在我的问题中,我写的是IIS 10,而不是Express。这两个应用程序都不是从Visual Studio执行的。
await Task.Delay(500);