深度探祕.NET 5.0

今年11月10號 .NET 5.0 如約而至。這是.NET All in one後的第一個版本,雖然不是LTS(Long term support)版本,可是是生產環境可用的。linux

有着微軟的背書:微軟從.NET Preview 1就開始在本身的網站上運行.NET 5, (Bing.com、dot.net已升級並運行了數個月),同時早期的.NET Core版本能夠直接升級到.NET 5.  因此你們是能夠放心使用的。android

接下來,咱們深刻了解一下.NET 5.0此次帶來了哪些新的特性。ios

1、.NET 5.0 的一些亮點(Highlights)git

1. 經過線上(生產環境)測試(battle-tested) : .NET5.0 經過在Bing.com和dot.net 託管運行數個月,全面經過了線上驗證,這證實這個版本是生產可用的github

2. 性能大幅提高:GC、JIT、正則表達式、多線程和異步處理、集合、LINQ、網絡訪問、JSON序列化、gRPC等等,瞭解詳細能夠訪問正則表達式

3. C# 9和F# 9 的語言提高:例如C#9的頂級程序和記錄record,F#5提供了交互式編程,並提升了.NET的性能。算法

4. .NET庫加強了Json序列化,正則表達式和HTTP(HTTP 1.1,HTTP / 2)的性能。這一點在第二條中已經有所涉及。docker

5. P95 的延遲有所減小,得益於GC、分層編譯和其餘組件的一些改進編程

6.更好、更靈活的應用部署選項:ClickOnce客戶端應用程序發佈,單文件應用程序,減少的容器映像大小以及添加的Server Core容器映像。json

7.平臺支持的範圍進一步擴展Windows Arm64WebAssembly

 2、再看統一平臺的願景

   2019年5月6號,微軟發佈了.NET 5.0 統一平臺的願景:未來只會有一個.NET,您將可使用它來定位Windows,Linux,macOS,iOS,Android,tvOS,watchOS和WebAssembly等。

   

   實現這一願景的第一步是整合.NET倉庫,即:整合關鍵的.NET代碼庫, 這是爲.NET運行庫和庫提供一個存儲庫是在各處交付相同產品的前提。Blazor就是代碼合併和.NET統一的最佳示例:Blazor WebAssembly的運行時和庫如今是從合併的dotnet /運行時倉庫中構建的。這意味着服務器上的Blazor WebAssembly和Blazor使用與徹底相同的代碼List<T>。

   代碼整合後,.NET Framework怎麼辦?

   .NET Framework仍然是受支持的Microsoft產品,而且每一個新版本的Windows都將繼續支持.NET Framework。去年,微軟宣佈已中止向.NET Framework添加新功能,逐步向.NET Core添加更多的.NET Framework API。

   這就意味着,.NET Framework已經停更了,版本目前停留在.NET Framework 4.8. 

   這也是沒辦法的事情,統一後的.NET, 從.NET5.0開始迭代了。此次.NET 5.0的Release列表也能發現這個狀況:

   

    在上述狀況下,目前是將.NET Framework升級到.NET Core的最佳時機了。若是比較在乎LTS版本,也能夠等到明年.NET 6統一升級。對於此,微軟的建議是:

    對於.NET Framework客戶端開發人員,.NET 5.0支持Windows窗體和WPF。

    對於.NET Framework服務器開發人員, 若是採用ASP.NET Core才能使用.NET 5.0。

    對於Web Forms開發人員,Blazor經過高效且更加現代的實現方式提供相似的開發人員體驗。

    對於WCF服務器和Workflow用戶能夠查看支持這些框架的社區項目

    以上,對於統一後的.NET 5.0, 廣大.NET Developers 能夠放心、開心地去擁抱此次升級和統一,這表明了.NET的將來。

 3、深刻了解一下編程語言層面的提高(C# 9 和 F# 5)

   C#9和F#5是.NET 5.0版本的一部分,被包含在.NET 5.0 SDK中。接下來詳細看一下C# 9 的一些語言新特性(F# 5用的比較少,再也不作詳細介紹):

  1. Top-level programs 頂級程序

   你們會問這是什麼?這是在頂級編寫程序的一種更簡單的方式:一個更簡單的 Program.cs 文件。

   咱們知道,原先在Program類中,必須有Main函數,這是程序的一個EntryPoint入口。

using System;

namespace NET5Demo { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }

   .NET 5引入Top-level programs 後,咱們不須要寫Main函數了。能夠直接這麼寫:

System.Console.WriteLine("Hello World!");

 你們會有疑問,真的沒有Main函數了嗎?其實這是個語法糖,咱們經過IL Spy看一下反編譯後的代碼:

 

  2. 邏輯模式和屬性模式匹配(Logical and property patterns)

  咱們可使用not or and 實現更強的更靈活的邏輯模式匹配:

  先看一個邏輯匹配的Demo:

var input = Console.ReadKey();
if (input.KeyChar is 'Y' or 'y') { Console.WriteLine("You choosed yes!"); }

  再看一個Switch的Demo:

int score = 90;
switch (score) { case 0: Console.WriteLine("0分."); break; case > 0 and <= 60: Console.WriteLine("合格."); break; case > 60 and <= 80: Console.WriteLine("優秀."); break; case > 80 and <= 100: Console.WriteLine("卓越."); break; }

 屬性模式匹配:經過兩個{},實現對對象屬性的模式匹配。

Type type = Type.GetType("System.String");
if (type is not null and { FullName: "System.String" }) { Console.WriteLine("It's type is System.String."); }

  3. record類型

  record是一個新增的引用類型,與class很像,那麼你們會問?爲何增長一個record類型呢?它的使用場景是什麼呢?

  答案:爲了方便比較數據是否一致。咱們寫個代碼示意一下:

  假設咱們有個User類,包含ID、Name、Gender、Tel幾個屬性,若是咱們要對比2個User對象是否相等,咱們可能須要逐個屬性對比,或重寫Equals、GetHashCode方法。

  那麼若是咱們用record類型呢?

record User(int Id, string Name, int Gender, string Tel);

 作個對象對比的Demo:

 
 

var userA = new User(1, "小米", 1, "123456789");
var userB = new User(1, "小米", 1, "123456789");

if (userA == userB)
{
    Console.WriteLine("這是一個用戶."); }

 總結一下:record類型讓開發省去了重寫相等比較的業務邏輯,同時簡化了類型定義和初始化

 4. 可空註解的增長和改進

  目前.NET library 類庫,已經全面設置了是否可空註解。其實這個特性其實在C# 8.0已經引入:C#8.0 引入了「可爲空引用類型」和「不可爲空引用類型」,使你可以對引用類型變量的屬性做出重要聲明 :

  #nullable enable
  class A{ }

  即.NET 5.0的類庫中已經全面更新了這個註解,方便開發時進行查看。

  同時,此次引入新的成員是否爲空的註解:MemberNotNull 和 MemberNotNullWhen,例如如下的代碼:

class UserManager
{
    User user = new User(1, "小米", 1, "123456789"); [MemberNotNull(nameof(user))] public string GetUserName(string id) { return user.Name; } }

  編譯器會智能提示:CS8602警告

  

 4、工具類的新變化

  .NET 5.0 改進了Windows窗體設計器,更改了目標框架適用於.NET 5.0及更高版本的方式,更改了WinRT的支持方式,以及其餘的一些改進。

  1. Windows窗體設計器:winform設計器

   Windows Forms設計器(用於.NET Core 3.1和.NET 5.0)已經在Visual Studio 16.8中進行了更新,如今支持全部Windows Forms控件。它還支持WinForms控件的Telerik UI。設計器包括您指望的全部設計器功能,包括:拖放,選擇,移動和調整大小,剪切/複製/粘貼/刪除控件,與屬性窗口集成,事件生成等。數據綁定和對更普遍的第三方控件的支持即將推出。

   

   2. .NET 5.0目標框架

   新增一個Console類型工程後,選擇目標框架是.NET 5.0, 其Project文件內容是這樣的:

   

   新增一個Windows窗體應用工程後,選擇目標框架是.NET 5.0, 其Project文件內容是這樣的:

   

   Windows桌面API(包括Windows窗體,WPF和WinRT)僅在定位時可用net5.0-windows。同時也能夠指定操做系統版本,例如net5.0-windows7或net5.0-windows10.0.17763.0(對於Windows October 2018 Update)。

   若是要使用WinRT API,則須要定位Windows 10版本。

   總結一下:

  • net5.0 是.NET 5.0的新目標框架綽號,Target Framework Moniker(TFM)。
  • net5.0結合並替換netcoreapp和netstandard TFM。
  • net5.0支持.NET Framework兼容模式
  • net5.0-windows 將用於公開Windows特定功能,包括Windows窗體,WPF和WinRT API。
  • .NET 6.0將使用相同的方法,並帶有net6.0和將添加net6.0-ios和net6.0-android。
  • 特定於操做系統的TFM能夠包含操做系統版本號,例如net6.0-ios14。
  • 可移植的API(如ASP.NET Core)可與一塊兒使用net5.0。帶有的Xamarin形式也是如此net6.0。

  3. WinRT Interop的重大改進

   在以Windows API爲目標這一主題上,微軟已經移至一個新模型,以做爲.NET 5.0的一部分來支持WinRT API。這包括調用API(在任一方向上; CLR <==> WinRT),兩個類型系統之間的數據封送處理以及打算在類型系統或ABI邊界上統一對待的類型的統一(即「投影類型」 」,IEnumerable<T>而且IIterable<T>是示例)。

   從.NET 5.0開始,原有的WinRT互操做體系已被移除。這是一個巨大的變化。這意味着使用WinRT和.NET Core 3.x的應用程序和庫須要從新開發對接,而且不能按原樣在.NET 5.0上運行。

   使用WinRT API的庫將須要多目標來管理.NET Core 3.1和.NET 5.0之間的這種差別。

   將來,.NET 將依靠Windows中的WinRT團隊提供的新CsWinRT工具。它生成基於C#的WinRT互操做程序集,能夠經過NuGet交付該程序集。Windows團隊正是針對Windows中的WinRT API所作的。但願將WinRT(在Windows上)用做互操做系統的任何人均可以使用該工具,以將本機API公開給.NET或將.NET API公開給本機代碼。

   關於CsWinRT工具,已經發布了1.0版本,具體能夠參考連接:https://blogs.windows.com/windowsdeveloper/2020/11/10/announcing-c-winrt-version-1-0-with-the-net-5-ga-release/

   4. .NET Native Export/ .NET 本地導出

    即本機二進制文件啓用導出功能。

   .NET 開發團隊的Aaron Robinson一直在從事.NET Native Exports項目,該項目爲將.NET組件做爲本機庫發佈提供了更完整的體驗。

   .NET Native導出項目可以實現:

  • 公開自定義的本地出口。
  • 不須要像COM這樣的高級互操做技術。
  • 跨平臺工做

   相似的實現技術,還有:

   5. 事件管道

   事件管道是在.NET Core 2.2中添加的新子系統和API,能夠在任何操做系統上執行性能和其餘診斷調查。

   在.NET 5.0中,事件管道已獲得擴展,以使事件探查器可以寫入事件管道事件。

   對於之前依靠ETW(在Windows上)監視應用程序行爲和性能的分析探查器,來講是一個很好的方案和選擇。

   這裏不作詳細展開了。

   6. 轉儲調試,Dump分析調試

   調試託管代碼須要瞭解託管對象和構造。數據訪問組件(DAC)是運行時執行引擎的子集,該引擎具備這些構造的知識,而且能夠在沒有運行時的狀況下訪問這些託管對象。

   如今,可使用WinDBG或Windows在Windows上分析在Linux上收集的.NET Core進程轉儲dotnet dump analyze。

   本次發佈還增長了對從macOS上運行的.NET進程捕獲ELF轉儲的支持。因爲ELF不是lldbmacOS上的本機可執行文件(像這樣的本地調試器將沒法與這些轉儲一塊兒使用)文件格式,所以咱們將其設爲啓用功能。

   要在macOS上支持轉儲收集,請設置環境變量COMPlus_DbgEnableElfDumpOnMacOS=1。可使用來分析產生的轉儲dotnet dump analyze。

   7. 打印環境信息

   隨着.NET擴展了對新操做系統和芯片體系結構的支持,有時須要一種打印環境信息的方法。.NET 5.0 建立了一個簡單的.NET工具來執行此操做,稱爲dotnet-runtimeinfo。可使用如下命令安裝和運行該工具:

dotnet tool install -g dotnet-runtimeinfo
dotnet-runtimeinfo

    

   5、運行時和類庫的提高

    1. RyuJIT的代碼質量提高

    能夠參考這個連接:Performance Improvements in RyuJIT in .NET Core and .NET Framework

    2. GC垃圾回收

  • Card mark stealing – dotnet/coreclr #25986  Server GC (on different threads) can now work-steal while marking gen0/1 objects held live by older generation objects. This means that ephemeral GC pauses are shorter for scenarios where some GC threads took much longer to mark than others.  ServerGC 中標記階段的耗時更短了
  • Introducing Pinned Object Heap – dotnet/runtime #32283 — Adds the Pinned Object Heap (POH). This new heap (a peer to the Large Object Heap (LOH)) will allow the GC to manage pinned objects separately, and as a result avoid the negative effects of pinned objects on the generational heaps. 新增固定對象堆(POH)。此新堆(與大對象堆(LOH)對等)將容許GC單獨管理固定對象,從而避免固定對象對堆的負面影響。
  • Allow allocating large object from free list while background sweeping SOH — Enabled LOH allocations using the free list while BGC is sweeping SOH. Previously this was only using end of segment space on LOH. This allowed for better heap usage.容許在後臺掃描SOH時從空閒列表中分配大對象
  • Background GC suspension fixes – dotnet/coreclr #27729 — Suspension fixes to reduce time for both BGC and user threads to be suspended. This reduces the total time it takes to suspend managed threads before a GC can happen. dotnet/coreclr #27578 also contributes to the same outcome. 掛起修復程序可減小BGC和用戶線程掛起的時間。這樣能夠減小發生GC以前掛起託管線程所需的總時間。
  • Fix named cgroup handling in docker — Added support to read limits from named cgroups. Previously we only read from the global one. 修復了docker中命名cgroup處理的問題—添加了對從命名cgroups讀取限制的支持
  • Optimize vectorized sorting – dotnet/runtime #37159 — vectorized mark list sorting in GC which reduces the ephemeral GC pause time (also dotnet/runtime #40613). GC中的矢量化標記列表排序,減小了短暫的GC暫停時間
  • Generational aware analysis – dotnet/runtime #40322 — generational aware analysis that allows you to determine what old generation objects hold on to younger generation objects thus making them survive and contribute to ephemeral GC pause time.GC代感知分析,可以肯定哪些舊世代對象保留在年輕代對象上,從而使它們得以生存並有助於短暫的GC暫停時間。
  • Optimize decommitting GC heap memory pages – dotnet/runtime #35896 — optimized decommit, much better decommit logic and for Server GC took decommit completely out of the 「stop the world」 phase which reduced blocking GC pause time.優化了取消受權,更好的取消受權邏輯,對於Server GC,徹底取消了「中止一切」階段的受權,從而減小了阻塞GC的暫停時間

     總體總結一下,Server GC延遲更低了,CPU消耗更少、性能更好了。

     3. Windows Arm64的支持

     .NET應用程序如今能夠在Windows Arm64上本機運行。在.NET Core 3.0中添加的對Linux Arm64的支持(對glibc和musl的支持)。使用.NET 5.0,能夠在Windows Arm64設備(例如Surface Pro X)上開發和運行應用程序。也能夠經過x86仿真在Windows Arm64上運行.NET Core和.NET Framework應用程序。可是本機運行Arm64具備更好的性能。

     同時,.NET 5.0 SDK當前在Windows Arm64上不包含Windows桌面組件-Windows窗體和WPF。Windows Arm64上支持SDK,控制檯和ASP.NET Core應用程序,但Windows桌面組件不支持。

     4. Arm64性能優化

     .NET 5.0 中主要針對Arm64平臺作了如下優化:

  • 調整Arm64的JIT優化(示例
  • 啓用並利用Arm64硬件內部函數(示例)。
  • 調整Arm64庫中對性能相當重要的算法(示例)。

     更多詳細信息,請參見在.NET 5.0中提升Arm64性能

      5. P95 +延遲改進

      Stack Overflow的一位工程師Nick Craver最近分享了他們升級.NET Core後,對延遲的改進:

      問題頁面的展示時間中值從大約21毫秒(因爲GC而有所增長)降至約15毫秒。

      第95個百分位數從〜40ms降低到〜30ms(相同測量)。第99位從〜60ms降至〜45ms。

      .NET項目組的解讀是這樣的:固定對象一直是GC性能的長期挑戰,由於它們會加速(或致使)內存碎片。.NET 5.0爲固定對象添加了新的GC堆。該固定對象堆是基於這樣的假設(以空間換時間),他們的存在會致使不相稱的性能挑戰極少數固定的對象。將固定的對象(尤爲是由.NET庫做爲實現細節建立的對象)移動到惟一的區域是有意義的,而垃圾回收代的GC堆幾乎沒有或沒有固定的對象,所以具備更高的性能。

       6. 分層編譯性能改進

       關於分層編譯,你們能夠參考這個鏈接:https://devblogs.microsoft.com/dotnet/tiered-compilation-preview-in-net-core-2-1

       在.NET 5.0中對分層編譯進行了兩項重大改進。下面這2段有點複雜,也比較晦澀

      分層編譯的主要機制是調用計數。一旦某個方法被調用了n次,運行時就會要求JIT以更高的質量從新編譯該方法。從最先的性能分析中,發現採用計數機制太慢,可是沒有找到解決該問題的直接方法。.NET 5.0中改進了分層JIT編譯所使用的調用計數機制,以平滑啓動期間的性能。在過去的發行版中,已經發如今進程生命週期的前10到15秒鐘內,性能會發生不可預測的變化(主要是針對Web服務器)。目前應該已經解決了。

     另外一個性能挑戰是對具備循環的方法使用分層編譯。根本的問題是,您可使用帶有循環屢次的循環的冷方法(僅調用一次或幾回; $ lt; n)。咱們稱這種病理狀況爲「冷方法」。熱循環」。能夠想象Main應用程序的方法會發生這種狀況。結果,默認狀況下,咱們禁用了帶循環方法的分層編譯。相反,使應用程序能夠選擇使用帶循環的分層編譯。在某些狀況下看到了個位數的高性能改進後,PowerShell就是選擇執行此操做的應用程序。

     爲了更好地解決循環問題,.NET 實現了棧上替換(OSR)。這相似於Java虛擬機具備的同名功能。OSR容許在方法執行過程當中從新編譯當前正在運行的方法執行的代碼,而這些方法是「堆棧上」活動的。該功能目前處於試驗和選擇啓用狀態,而且僅在x64上可用。

     要使用OSR,必須啓用多個功能。目前.NET 5.0中沒有啓用OSR,這個功能還沒有決定在生產環境中是否啓用,因此這個技術點,瞭解便可。

      7. JSON序列化 System.Text.Json

      .NET 5.0 對System.Text.Json進行了顯着改進,以提升性能和可靠性,同時API儘量地和Newtonsoft.Json相似。它還包括對將JSON對象反序列化對record類型的支持。

      同時微軟提供了System.Text.Json替換Newtonsoft.Json的遷移指南。該指南詳細闡明瞭這兩個API之間的關係。

      如何從 Newtonsoft.Json 遷移到 System.Text.Json

      JsonSerializer.NET 5.0中的性能顯着提升。Stephen Toub在.NET 5中的性能改進中介紹了一些JsonSerializer改進

  6、應用程序部署

    應用程序開發完成後,根據實際的須要,可能會部署到Web服務器,雲服務或客戶端計算機,或者使用Azure DevOps或GitHub Actions之類的服務進行CI/CD。

    .NET 5.0專一於改善單個文件應用程序,減少docker多階段構建的容器大小,併爲使用.NET Core部署ClickOnce應用程序提供更好的支持。

    1. 容器

    與容器的交互協做很是重要。這個版本中添加了OpenTelemetry支持,能夠從應用程序中捕獲分佈式跟蹤和指標。dotnet-monitor是一個新工具,能夠做爲從.NET進程訪問診斷信息的主要工具。特別是,咱們已經開始構建dotnet-monitor的容器變體,您能夠將其用做應用程序sidecar。同時,.NET項目組正在構建dotnet / tye,以提升微服務開發人員在開發和部署到Kubernetes環境中的效率。

   .NET運行時如今支持cgroup v2,這個API預計將在2020年之後成爲與容器相關的重要API。Docker當前使用cgroup v1(.NET已支持)。相比之下,cgroup v2比cgroup v1更簡單,更有效且更安全。.NET 5.0將在cgroup v2環境中正常工做。

   除了Nano Server,微軟還將發佈Windows Server Core映像,努力減少Windows Server Core映像的大小。

    更小的體積、更低的成本、更快的啓動性能。.NET 5.0中將SDK映像從新創建在ASP.NET映像之上,而不是buildpack-deps,這樣能夠顯着減少在多階段構建方案中提取的聚合映像的大小。

 

   

    2. 單文件應用    

    單個文件應用程序做爲單個文件發佈和部署。該應用程序及其依賴項都包含在該文件中。當應用程序運行時,依賴項直接從該文件加載到內存中(不影響性能)。

    在.NET 5.0中,單個文件應用程序主要集中在Linux上。它們能夠是框架相關的,也能夠是獨立的。依賴於全局安裝的.NET運行時,依賴於框架的單個文件應用程序可能很小。自包含的單文件應用程序較大(因爲帶有運行時),但不須要做爲安裝前步驟就安裝.NET運行時,所以能夠正常工做。一般,依賴框架對開發和企業環境有利,而對於ISV,獨立包含一般是更好的選擇。

    .NET Core 3.1製做了一個單文件應用程序版本。它將二進制文件打包到一個文件中以進行部署,而後將這些文件解壓縮到一個臨時目錄中以加載並執行它們。在某些狀況下,這種方法會更好,可是但願爲5.0構建的解決方案將是首選,而且會受到歡迎。  

    可使用如下命令來生成單文件應用程序:

框架相關的單文件應用程序:
dotnet publish -r linux-x64 --self-contained false /p:PublishSingleFile=true
自包含的單文件應用程序:
dotnet publish -r linux-x64 --self-contained true /p:PublishSingleFile=true

  3. ClickOnce

    ClickOnce一直是流行的.NET部署選項,歷史也比較悠久了。.NET Core 3.1和.NET 5.0 Windows應用程序如今支持它。   

    

     以上是.NET 5.0 發佈後的技術梳理和整理,.NET 5.0做爲.NET技術棧上近幾年一個重量級的里程碑,是All in one,統一平臺的第一個版本。如今有微軟的背書,微軟從.NET Preview 1就開始在本身的網站上運行.NET 5, (Bing.com、dot.net已升級並運行了數個月),同時早期的.NET Core版本能夠直接升級到.NET 5. 因此你們能夠放心使用的。也推薦你們逐步遷移升級到.NET 5.0.

    參考連接:

   https://devblogs.microsoft.com/dotnet/announcing-net-5-0?WT.mc_id=DT-MVP-5003918

   https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/#json?WT.mc_id=DT-MVP-5003918

   https://blogs.windows.com/windowsdeveloper/2020/11/10/announcing-c-winrt-version-1-0-with-the-net-5-ga-release?WT.mc_id=DT-MVP-5003918

 

周國慶

2020/11/15

相關文章
相關標籤/搜索