1. 程式人生 > 實用技巧 >對Asp.net WebApi中非同步(async+await)介面實際使用及相關思考(示例給出了get,post,提交檔案,非同步介面等實踐).

對Asp.net WebApi中非同步(async+await)介面實際使用及相關思考(示例給出了get,post,提交檔案,非同步介面等實踐).

【很多初學者的疑問】

為何作為web api這樣的天然的併發應用,還需要在controller的action上宣告使用async這些呢?

<參考解答>

在 web 伺服器上,.NET Framework 維護用於處理 ASP.NET 請求的執行緒池。 當請求到達時,將排程池中的執行緒以處理該請求。 如果以同步方式處理請求,則處理請求的執行緒將在處理請求時處於繁忙狀態,並且該執行緒無法處理其他請求。
如果請求發出需要兩秒鐘時間才能完成的 web 服務呼叫,則該請求將需要兩秒鐘,無論是同步執行還是非同步執行。 但是,在非同步呼叫期間,執行緒在等待第一個請求完成時不會被阻止響應其他請求。 因此,當有多個併發請求呼叫長時間執行的操作時,非同步請求會阻止請求佇列和執行緒池的增長。

[注]總的來說,對單個客戶端請求來說,它感受到的速度,響應時間並沒有因為使用非同步而提升,但對整個伺服器來說,因為執行緒在非同步場景下等待的同時還在服務其它的執行緒,因此執行緒數不會增長太快,進而不會輕易達到繁忙狀態。

【給出一個自己寫的分析程式碼】

using ConfigLab.Comp;
using ConfigLab.Comp.HttpRequestTools.HttpClient;
using ConfigLab.Comp.MetaData;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Web.Hosting; using System.Web.Http; namespace ConfigLab.WebApiProject.Controllers { /// <summary> /// 功能簡介:web api中的非同步介面體會 /// 建立時間:2020-8-23 /// 建立人:pcw
/// 備註:如果不需要所有介面有一個預設的 /api/*中的api這段,需要自行修改RouteConfig.cs中的路由設定 /// </summary> public class CommonAPIController : ApiController { /// <summary> /// https://localhost:44305/CommonAPI/getTest1?userid=u001 /// </summary> /// <param name="userid"></param> /// <returns></returns> public string getTest(string userid) { return $"test1_result(from web api):userid={userid}"; } /// <summary> /// https://localhost:44305/CommonAPI/getTest2 /// 引數:userid=u002&optype=add /// </summary> /// <param name="data"></param> /// <returns></returns> [HttpPost] public string postTest([FromBody] userActin data) { return $"test2_result(from web api):userid={data.userid},optype={data.optype}"; } /// <summary> /// 請求地址:https://localhost:44305/CommonAPI/postListByAsync /// 引數:userid=u002&optype=add /// 返回:多個網站的返回值 /// </summary> /// <param name="data"></param> /// <returns></returns> public async Task<List<ResponseResult>> postListByAsync([FromBody] userActin data) { List<ResponseResult> listResult = new List<ResponseResult>(); HttpClientAssisterAsync httpAssist = new HttpClientAssisterAsync();//介紹(https://www.cnblogs.com/taohuadaozhu/p/13548266.html),Nuget 搜尋安裝: ConfigLab.Comp即可使用 Task<ResponseResult> tsk_rrs = httpAssist.SendRequestByGet("http://www.baidu.com"); await tsk_rrs; listResult.Add(tsk_rrs.Result); tsk_rrs = httpAssist.SendRequestByGet("http://www.ifeng.com"); await tsk_rrs; listResult.Add(tsk_rrs.Result); return listResult; } /// <summary> /// 請求地址:https://localhost:44305/CommonAPI/SaveFile?projectId=p001&userid=u003 /// 附加引數:檔案流 /// </summary> /// <param name="projectId"></param> /// <param name="userid"></param> /// <returns></returns> public async Task<RunResult> SaveFile(string projectId, string userid) { RunResult rrs = new RunResult(); if (!Request.Content.IsMimeMultipartContent()) { //throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); return new RunResult() { RunCode = -1, RunMsg = "HttpStatusCode.UnsupportedMediaType" }; } //string root = Path.Combine(HostingEnvironment.MapPath(ConfigurationManager.AppSettings["FileStorePath"]), DateTime.Now.ToShortDateString(), projectId); string root = Path.Combine(ConfigurationManager.AppSettings["FileStorePath"], DateTime.Now.ToShortDateString(), projectId); if (!Directory.Exists(root)) Directory.CreateDirectory(root); MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root); try { await Request.Content.ReadAsMultipartAsync(provider); return new RunResult() { RunCode = 0, RunMsg = "" }; } catch (Exception ex) { rrs.RunCode = -2; rrs.RunMsg = $"獲取客戶端上傳的檔案流失敗,ex.msg={ex.Message},ex.stacktrace={ex.StackTrace}"; } return rrs; } } /// <summary> /// 測試用的post引數物件 /// </summary> public class userActin { public string userid { get; set; } public string optype { get; set; } } }