use*_*111 7 c# asynchronous async-await asp.net-web-api
我试图向大三学生展示异步编程的重要性,使用async和await.出于此目的,我创建了一个带有一个控制器和两个GET操作的ASP.NET Web API项目.一个GET操作是同步的,另一个是异步的.我想证明,在同步阻塞i/o调用的情况下,所有可用的ASP.NET工作线程都在等待并且没有任何用处,同时当更多请求到达时,它们将超时,因为所有可用的线程都是等待I/O线程完成.问题是,我的下面的代码片段部分传达了这一点.它可以在异步调用的情况下正常工作,但不能用于同步调用.如果我取消注释已注释掉的代码行,它就不会发生,ASP.NET运行时可以处理更多的线程.代码段如下:
public class TestController : ApiController
{
// -> Uncommenting the below method proves my point of scalability <-
//public async Task<string> Get()
//{
// CodeHolder obj = new CodeHolder();
// return await obj.AsyncData();
//}
// -> Uncommenting the below method doesn't enforce time outs, rather waits <-
public string Get()
{
CodeHolder obj = new CodeHolder();
return obj.SyncData();
}
}
class CodeHolder
{
public string SyncData()
{
Task.Delay(10000).Wait();
return $"I am returned from Sync after waiting for 10 second at {DateTime.Now.ToString("HH:mm:ss:fffffff")}";
}
public async Task<string> AsyncData()
{
await System.Threading.Tasks.Task.Delay(10000);
return $"I am returned from Async after semi-waiting for 10 second at {DateTime.Now.ToString("HH:mm:ss:fffffff")}";
}
}
Run Code Online (Sandbox Code Playgroud)
虽然我试图提出这一点,但是随着同步调用需要很长时间才能完成,但我想知道为什么请求被保留在队列中而不是超时.我正在使用JMeter向我的Web API服务发送250个并发HTTP请求,但它们永远不会超时而是等待并完成,尽管延迟非常大(约250秒).
顺便说一句,在异步版本中,所有响应都在大约10秒内返回.
小智 -1
您可以证明同步版本比异步版本使用线程池中更多的线程。
这就是我想出的演示 Web 控制器的方法:
[Route("api/v1/[controller]")]
public class ThreadController : Controller
{
private static readonly object locker = new object();
private static HashSet<int> syncIds = new HashSet<int>();
[HttpGet("sync")]
public ActionResult<string> GetSync()
{
var id = Thread.CurrentThread.ManagedThreadId;
lock (locker)
{
syncIds.Add(id);
}
Task.Delay(10000).Wait();
return $"I am returned from Sync after waiting for 10 second at {DateTime.Now.ToString("HH:mm:ss:fffffff")}";
}
[HttpGet("sync/count")]
public ActionResult<int> CountSync()
{
lock (locker)
{
int count = syncIds.Count;
syncIds.Clear();
return count;
}
}
private static HashSet<int> asyncIds = new HashSet<int>();
[HttpGet("async")]
public async Task<ActionResult<string>> GetAsync()
{
var id = Thread.CurrentThread.ManagedThreadId;
lock (locker)
{
asyncIds.Add(id);
}
await Task.Delay(10000);
return $"I am returned from Async after waiting for 10 second at {DateTime.Now.ToString("HH:mm:ss:fffffff")}";
}
[HttpGet("async/count")]
public ActionResult<int> CountAsync()
{
lock (locker)
{
int count = asyncIds.Count;
asyncIds.Clear();
return count;
}
}
}
Run Code Online (Sandbox Code Playgroud)
一个控制台应用程序来模拟请求(我给出了 150 毫秒的延迟,以便有时间到达等待块):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
using (var http = new HttpClient() )
{
var stopwatch = Stopwatch.StartNew();
var sync = new List<Task>();
for (int i = 0; i < 20; i++)
{
sync.Add(http.GetAsync("http://localhost:5000/api/v1/thread/sync") );
Thread.Sleep(150);
}
Task.WaitAll(sync.ToArray() );
stopwatch.Stop();
Console.WriteLine("Sync used " + http.GetAsync("http://localhost:5000/api/v1/thread/sync/count").Result.Content.ReadAsStringAsync().Result + " threads in " + stopwatch.ElapsedMilliseconds + "ms");
stopwatch.Restart();
var async = new List<Task>();
for (int i = 0; i < 20; i++)
{
async.Add(http.GetAsync("http://localhost:5000/api/v1/thread/async") );
Thread.Sleep(150);
}
Task.WaitAll(async.ToArray() );
stopwatch.Stop();
Console.WriteLine("Async used " + http.GetAsync("http://localhost:5000/api/v1/thread/async/count").Result.Content.ReadAsStringAsync().Result + " threads in " + stopwatch.ElapsedMilliseconds + "ms");
}
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我得到的结果:
第一次运行:
Sync used 19 threads in 22412ms
Async used 8 threads in 12911ms
Run Code Online (Sandbox Code Playgroud)
第二次运行:
Sync used 18 threads in 21083ms
Async used 10 threads in 12921ms
Run Code Online (Sandbox Code Playgroud)
第三次运行:
Sync used 20 threads in 13578ms
Async used 10 threads in 12899ms
Run Code Online (Sandbox Code Playgroud)
第四次运行:
Sync used 18 threads in 21018ms
Async used 5 threads in 12889ms
Run Code Online (Sandbox Code Playgroud)