在上一篇介紹了 ASP.NET Core Task block 檢測器 - Ben.BlockingDetector ,可以在 ASP.NET Runtime 時偵測 task block 的情境並透過 log 輸出,但預防的工作總是希望越早進行越好,越快發現就更早的預防不好的寫法及早修改,本篇要介紹的是非同步代碼分析套件,可以分析靜態代碼中可能用法"不正確"的代碼,可以若有問題或是錯誤的地方歡迎高手大大給予指導或討論。
開啟 Visual Studio 的 Nuget 搜尋 Microsoft.VisualStudio.Threading.Analyzers 找到後下載
或是可以透過 Nuget Package Console 輸入下列指令
安裝完畢之後到專案檔底下確認是否有安裝成功
- Install-Package Microsoft.VisualStudio.Threading.Analyzers -Version 16.3.52
- <ItemGroup>
- <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.3.52" />
- </ItemGroup>
分析指南
在安裝完後會針對目前專案靜態代碼進行分析,主要目的是透過此套件來檢查有關 Threads 和非同步的常見錯誤或是潛在問題,從官方 Github 說明頁面可以得知分析項目有包含以下內容
並且在規則中有分嚴重程度,且根據分析工具抓到靜態代碼的內容來給於提示與建議,嚴重性分為三類
加入範例代碼
透過上述了解了規則之後,接下來我們加入 Thread blocking 的代碼來簡單示範分析結果,在新增的專案 controller 類別加入常見的 Task.Result 與 Task.Wait 阻塞方法
- public class ValuesController : ControllerBase
- {
- // GET api/values
- [HttpGet]
- public async void Get()
- {
- var result = await Task.Run(() => BlockingTask());
- }
- private static int BlockingTask()
- {
- // Detected blocking
- return MethodAsync().Result;
- }
- private static async Task<int> MethodAsync()
- {
- await Task.Delay(1000);
- return 5;
- }
- //private static object _lock;
- [HttpGet("/")]
- public Task Slower()
- {
- // Detected blocking in called method
- return DoSomethingAsync();
- }
- private async Task DoSomethingAsync()
- {
- await Task.Delay(1000);
- // Detected blocking
- Task.Run(() => { Thread.Sleep(1000); }).Wait();
- }
- [HttpGet("/hello")]
- public string Hello()
- {
- // Not detected: Thread.Sleep happens at OS level
- Thread.Sleep(2000);
- return "Hello World";
- }
- [HttpGet("/hello-sync-over-async")]
- public string HelloSyncOverAsync()
- {
- // Detected blocking
- Task.Delay(2000).Wait();
- return "Hello World";
- }
- [HttpGet("/hello-async-over-sync")]
- public async Task<string> HelloAsyncOverSync()
- {
- // Detected blocking in called method
- var result = await Task.Run(() => BlockingTask());
- return $"Hello World {result}";
- }
- [HttpGet("/hello-async")]
- public async Task<string> HelloAsync()
- {
- // No blocking :)
- await Task.Delay(2000);
- return "Hello World";
- }
- [HttpGet("/hello-async-precompleted")]
- public async Task<string> HelloAsyncPrecompleted()
- {
- Task<int> task = MethodAsync();
- if (!task.IsCompletedSuccessfully)
- {
- // Await completes the Task without blocking
- await task;
- }
- // The task is completed at this point so .Result doesn't trigger blocking
- var result = task.Result;
- return $"Hello World {result}";
- }
- }
分析建議
在安裝完套件之後,可以看到原本看似 peace 的靜態代碼在分析之後有潛在的風險,舉例來說用到 Task.Result ,就會說明目前寫法可能會造成 deadlock,建議改用 await 方式避免
或是使用 Task.Wait
或是方法也會有相關建議
透過此套件可以提早發現代碼中可能造成的問題,避免上線後才發現影響到用戶,在 Github 網頁有更詳細的介紹與說明,若有興趣了解細節運作可以查看其 Github source code : 傳送門,希望這篇文章有幫助到大家&喜歡 :)
0 意見:
張貼留言