ASP.NET MVC EF 中使用異步控制器


最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。

十年河東十年河西,莫欺少年窮

學無止境,精益求精  

為什么使用異步操作/線程池

 ASP.NET MVC 中為什么需要使用異步呢,IIS有一個線程池來處理用戶的請求,當一個新的請求過來時,將調度池中的線程以處理該請求,然而,但並發量很高的情況下,池中的線程已經不能夠滿足這么多的請求時候,池中的每一個線程都處於忙的狀態則在處理請求時將阻塞處理請求的線程,並且該線程不能對另一個請求提供服務,如果請求隊列已滿,則 Web 服務器會拒絕請求並處於 HTTP 503繁忙狀態。如果是處理一些高延遲,例如網絡操作,這樣的線程大多數只是等待狀態大部分時間是不做任何事情的,這樣的線程就可以使用異步編程更好的充分利用。

異步處理

例如:如果某個請求生成一個需要兩秒鍾來完成的網絡調用,則該請求無論是同步執行還是異步執行都需要兩秒鍾。 但是,在異步調用的過程中,服務器在等待第一個請求完成的過程中不會阻塞對其他請求的響應。 因此,當有許多請求調用長時間運行的操作時,異步請求可以防止出現請求排隊的情況。在.NET 4.5中最大線程池為 5000 .NET 4.5中也增加了 await與async關鍵字來簡化異步編程。

同步還是異步(摘錄MSDN)

通常,在滿足以下條件時使用同步管線:

  • 操作很簡單或運行時間很短。

  • 簡單性比效率更重要。

  • 此操作主要是 CPU 操作而不是包含大量的磁盤或網絡開銷的操作。 對 CPU 綁定操作使用異步操作方法未提供任何好處並且還導致更多的開銷。

通常,在滿足以下條件時使用異步管線:

  • 操作是網絡綁定的或 I/O 綁定的而不是 CPU 綁定的。

  • 測試顯示阻塞操作對於網站性能是一個瓶頸,並且通過對這些阻塞調用使用異步操作方法,IIS 可對更多的請求提供服務。

  • 並行性比代碼的簡單性更重要。

  • 您希望提供一種可讓用戶取消長時間運行的請求的機制。

ASP.NET MVC 中使用異步控制器

#region 1、異步請求
        
        [AsyncTimeout(1000)]
        public async Task<ActionResult> Index()
        {
            var data = await GetPageTaskAsync("http://163.com");
            return data;
        }

        public async Task<ActionResult> GetPageTaskAsync(string url)
        {
            try
            {
                using (var client = new HttpClient())
                {
                    await Task.Delay(3000);
                    var fetchTextTask = client.GetStringAsync(url);
                    return Json(new { fetchText = await fetchTextTask,error="NO" },JsonRequestBehavior.AllowGet);
                }
            }
            catch (WebException ex)
            {
                
                throw ex;
            }
        }

        #endregion

下面以EF為例進行說明:

代碼中高亮部分顯示了異步方法和同步方法的不同之處:

        public async Task<ActionResult> Index()
        {
            var departments = db.Departments.Include(d => d.Administrator);
            return View(await departments.ToListAsync());
        }

我們應用了四個更改來啟用實體框架數據庫執行異步查詢:

  • 該方法使用了async關鍵字,它告訴編譯器生成回調方法體的部分,並自動創建Task<ActionResult>返回對象。
  • 返回類型從ActionResult更改為Task<ActionResult>。Task<T>類型表示正在進行的任務具有類型為T的結果。
  • await關鍵字被應用到web服務調用。當編譯器看到有此關鍵字時,在后台將該方法分為兩個部分。第一部分結束於異步操作啟動,第二部分被放入一個操作完成時的回調方法。
  • 調用了ToList擴展方法的異步版本。

為什么只修改departments.ToList語句而不是departments= db.Departments語句?原因是只有被發送的數據庫執行的查詢或語句才能夠使用異步執行。departments=db.Departments語句設置了一個查詢,但直到調用ToList方法時該查詢都不會執行。因此,只有ToList方法是異步執行的。

在Details方法和Httpget的Edit和Delete方法中,Find方法是導致查詢被發送到數據庫進行檢索的方法,所以該方法是可以異步執行的。

public async Task<ActionResult> Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Department department = await db.Departments.FindAsync(id);
            if (department == null)
            {
                return HttpNotFound();
            }
            return View(department);
        }

在Create,HttpPost的Edit和DeleteConfirmed方法中,是SaveChanges方法導致命令執行,而像db.Department.Add(department)方法只是導致實體在內存中的修改。

public async Task<ActionResult> Create([Bind(Include="DepartmentID,Name,Budget,StartDate,InstructorID")] Department department)
        {
            if (ModelState.IsValid)
            {
                db.Departments.Add(department);
                await db.SaveChangesAsync();
                return RedirectToAction("Index");
            }

            ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "LastName", department.InstructorID);
            return View(department);
        }

程序正常地運行,就跟其他的控制器一樣。但在此控制器中,所有SQL查詢都是異步執行的。

當您在實體框架中使用異步編程要注意的一些事情:

  • 異步代碼不是線程安全的。換言之,不要使用同一個上下文實例以並行方式來執行多個操作。
  • 如果你想要利用異步代碼的性能優勢,請確保您正在使用的所有庫軟件包(例如分頁),在包中進行的數據庫查詢等任何實體框架方法也使用異步執行。

@陳卧龍的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM