只有累積,沒有奇蹟

2019年9月13日 星期五

[VisualStudio] ASP.NET Core 非同步代碼分析工具 - Threading.Analyzers

前言
在上一篇介紹了 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 說明頁面可以得知分析項目有包含以下內容
並且在規則中有分嚴重程度,且根據分析工具抓到靜態代碼的內容來給於提示與建議,嚴重性分為三類


  • Critical : Code issues that often result in deadlocks
  • Advisory : Code that may perform differently than intended or lead to occasional deadlocks
  • Guideline : Code that deviates from best practices and may limit the benefits of other analyzers



  • 加入範例代碼
    透過上述了解了規則之後,接下來我們加入 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 : 傳送門,希望這篇文章有幫助到大家&喜歡 :)

    參考
     vs-threading

    0 意見:

    張貼留言

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com