<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      熟悉而陌生的新朋友——IAsyncDisposable

      歡迎關注我們的新博客——碼睿鴨

      .NET Core 3.0的版本更新中,官方我們帶來了一個新的接口 IAsyncDisposable

      小伙伴一看肯定就知道,它和.NET中原有的IDisposable接口肯定有著密不可分分的關系,且一定是它的異步實現版本。

      那么.NET是為什么要在 .NET Core 3.0 (伴隨C# 8) 發布的同時,帶來該接口呢? 還有就是該異步版本和原來的IDispose有著什么樣的區別呢? 到底在哪種場景下我們能使用它呢?

      帶著這些問題,我們今天一起來認識一下這位"新朋友" —— IAsyncDisposable

      為了更好的了解它,讓我們先來回顧一下.NET中的資源釋放:

      .NET的資源釋放

      由于.NET強大的GC,對于托管資源來說(比如C#的類實例),它的釋放往往不需要開發人員來操心。

      但是在開發過程中,有時候我們需要涉及到非托管的資源,比如I/O操作,將緩沖區中的文本內容保存到文件中、網絡通訊,發送數據包等等。

      由于這些操作GC沒有辦法控制,所以也就沒有辦法來管理它們的生命周期。如果使用了非托管資源之后,沒有及時進行釋放資源,那么就會造成內存的泄漏問題。

      而.NET為我們提供了一些手段來進行資源釋放的操作:

      析構函數

      析構函數在C#中是一個語法糖,在構造函數前方加一個符號即代表使用析構函數 。

      public class ExampleClass
      {
      public ExampleClass()
      {
      }
      
      ~ExampleClass()	// 析構函數
      {
      // 釋放非托管資源
      }
      }
      

      當一個類申明了析構函數了之后,GC將會對它進行特殊的處理,當該實例的資源被GC回收之前會調用析構函數。(該部分內容本文將不做過多介紹)

      雖然析構函數方法在某些需要進行清理的情況下是有效的,但它有下面兩個嚴重的缺點:

      • 只有在GC檢測到某個對象可以被回收時才會調用該對象的終結方法,這發生在不再需要資源之后的某個不確定的時間。這樣一來,開發人員可以或希望釋放資源的時刻與資源實際被終結方法釋放的時刻之間會有一個延遲。如果程序需要使用許多稀缺資源(容易耗盡的資源)或不釋放資源的代價會很高(例如,大塊的非托管內存),那么這樣的延遲可能會讓人無法接受。
      • 當CLR需要調用終結方法時,它必須把回收對象內存的工作推遲到垃圾收集的下一輪(終結方法會在兩輪垃圾收集之間運行)。這意味著對象的內存會在很長一段時間內得不到釋放。

      因此,如果需要盡快回收非托管資源,或者資源很稀缺,或者對性能要求極高以至于無法接受在GC時增加額外開銷,那么在這些情況下完全依靠析構函數的方法可能不太合適。

      而框架提供了IDisposable接口,該接口為開發人員提供了一種手動釋放非托管資源的方法,可以用來立即釋放不再需要的非托管資源。

      IDisposable

      .NET Framework 1.1開始 ,.NET就為我們提供了IDispose接口。

      使用該接口,我們可以實現名為Dispose的方法,進行一些手動釋放資源的操作(包括托管資源和非托管資源)。

      public class ExampleClass:IDisposable
      {
      private Stream _memoryStream = new MemoryStream();
      
      public ExampleClass()
      {
      }
      
      public void Dispose()
      {
      // 釋放資源
      myList.Clear();
      myData = null;
      _memoryStream.Dispose();	
      }
      }
      

      在C#中,我們除了可以手動調用 xx.Dispose()方法來觸發釋放之外,還可以使用using的語法糖。

      當我們在 visual studio 中添加IDisposable接口時,它會提示我們使用是否使用“釋放模式”:

      101.png

      “釋放模式”所生成的代碼如下:

      protected virtual void Dispose(bool disposing)
      {
      if (!disposedValue)
      {
      if (disposing)
      {
      // TODO: 釋放托管狀態(托管對象)
      }
      
      // TODO: 釋放未托管的資源(未托管的對象)并重寫終結器
      // TODO: 將大型字段設置為 null
      disposedValue = true;
      }
      }
      
      // // TODO: 僅當“Dispose(bool disposing)”擁有用于釋放未托管資源的代碼時才替代終結器
      // ~ExampleClass()
      // {
      // // 不要更改此代碼。請將清理代碼放入“Dispose(bool disposing)”方法中
      // Dispose(disposing: false);
      // }
      
      public void Dispose()
      {
      // 不要更改此代碼。請將清理代碼放入“Dispose(bool disposing)”方法中
      Dispose(disposing: true);
      GC.SuppressFinalize(this);
      }
      

      釋放資源的代碼被放置在 Dispose(bool disposing) 方法中,你可以選用 析構函數 或者 IDisposable 來進行調用該方法。

      這里說一下:在 IDisposable 的實現中,有一句 GC.SuppressFinalize(this);。 這句話的意思是,告訴GC,不需要對該類的析構函數進行單獨處理了。也就是說,該類的析構函數將不會被調用。因為資源已經在 Dispose() 中被我清理了。

      異步時代

      .NET Core開始,就意味著.NET來到了一個全新的異步時代。無論是各種基礎類庫(比如System.IO)、AspNet Core、還是EFCore..... 它們都支持異步操作,應該說是推薦異步操作。

      在今天,假如一個新項目沒有使用 awaitasync。你都會覺得自己在寫假代碼??

      現在越來越多的開發者都愛上了這種異步方式:不阻止線程的執行,帶來高性能的同時還完全不需要更改原有的編碼習慣,可謂是兩全其美。

      所以從.NET Core 開始到現在的.NET 5 ,每一次版本更迭都會有一批API提供了異步的版本。

      IAsyncDisposable的誕生

      為了提供這樣一種機制讓使用者能夠執行資源密集型的處置操作,而不會長期阻塞GUI應用程序的主線程,我們讓操作成為了異步。

      同樣,釋放資源的時候我們能否成為異步呢? 假如一次釋放操作會占耗費太多的時間,那為什么我們不讓它去異步執行呢?

      為了解決這一問題,同時更好的完善.NET異步編程的體驗,IAsyncDisposable誕生了。

      它的用法與IDisposable非常的類似:

      public class ExampleClass : IAsyncDisposable
      {
      private Stream _memoryStream = new MemoryStream();
      
      public ExampleClass()
      {
      
      }
      
      public async ValueTask DisposeAsync()
      {
      await _memoryStream.DisposeAsync();
      }
      }
      

      當然,using的語法糖同樣適用于它。不過,由于它是異步編程的風格,在使用時記得添加await關鍵字:

      await using var s = new ExampleClass()
      {
      // doing
      };
      

      當然在 C# 8 以上,我們可以使用using作用域的簡化寫法:

      await using var s = new ExampleClass();
      // doing
      

      IAsyncDisposable與IDisposable的選擇

      有一個關鍵點是: IAsyncDisposable 其實并沒有繼承于 IDisposable

      這就意味著,我們可以選擇兩者中的任意一個,或者同時都要。

      那么我們到底該選擇哪一個呢?

      這個問題其實很類似于EF剛為大家提供SaveChangesAsync方法的時候,到底我們該選用SaveChangesAsync還是SaveChanges呢?

      在以往同步版本的代碼中,我們往往會選擇SaveChanges同步方法。 當來到了異步的環境,我們往往會選擇SaveChangesAsync

      所以在AspNet Core這個全流程異步的大環境下,我們的代碼潛移默化的就會更改為SaveChangesAsync

      IAsyncDisposable也是同理的,當我們處于異步的環境中,所使用的資源提供了異步釋放的接口,那么我們肯定就會自然而然的使用IAsyncDisposable

      .NET 5 之后,大部分的類都具有了IAsyncDisposable的實現。比如:

      • Utf8JsonWriterStreamWriter這些與文件操作有關的類;
      • DbContext這類數據庫操作類
      • Timer
      • 依賴注入的ServiceProvider
      • ………………

      接下來的.NET版本中,我們也會看到AspNet Core中的Controller 等對于IAsyncDisposable提供支持。

      102.png

      可以預測是,在未來的.NET發展中,全異步的發展是必然的。后面越來越的已有庫會支持異步的所有操作,包括IAsyncDisposable的使用也會越來越頻繁。

      Asp Net Core 依賴注入中的IAsyncDisposable

      對于咱們使用AspNet Core的開發人員來說,我們在大多數情況下都會依賴于框架所提供的依賴注入功能。

      而依賴注入框架,會在作用域釋放的時候,自動去調用所注入服務的釋放接口IDisposable

      比如我們把 DbContext 注入之后,其實就只管使用就行了,從來不會關心它的Dispose問題。 相對于傳統using(var dbContext = new MyDbContext)的方式要省心很多,也不會擔心忘記寫釋放而導致的數據庫連接未釋放的問題。

      那么,當IAsyncDisposable出現之后呢?會出現什么情況:

      public void ConfigureServices(IServiceCollection services)
      {
      services.AddControllers();
      
      services.AddScoped<DemoDisposableObject>();	// 注入測試類	
      }
      
      
      public class DemoDisposableObject : IAsyncDisposable
      {
      public ValueTask DisposeAsync()
      {
      code here 
      // 當完成一次http 請求后,該方法會自動調用
      }
      }
      

      當我們實現了IAsyncDisposable之后,會被自動調用。

      那么如果 IAsyncDisposableIDisposable 一同使用呢?

      public class DemoDisposableObject : IAsyncDisposable,IDisposable
      {
      public void Dispose()
      {
      code here 
      }
      
      public ValueTask DisposeAsync()
      {
      code here 
      }
      }
      

      這樣的結果是:只有DisposeAsync方法會被調用

      為什么會有這樣的結果呢? 讓我們一起來扒開它的面紗。

      以下代碼位于 AspNet Core源碼

      public class RequestServicesFeature : IServiceProvidersFeature, IDisposable, IAsyncDisposable
      {
      public IServiceProvider RequestServices
      {
      get
      {
      if (!_requestServicesSet && _scopeFactory != null)
      {
      _scope = _scopeFactory.CreateScope();
      ……………………
      }
      return _requestServices!;
      }
      }
      
      public ValueTask DisposeAsync()
      {
      switch (_scope)
      {
      case IAsyncDisposable asyncDisposable:
      var vt = asyncDisposable.DisposeAsync();
      ………………
      break;
      case IDisposable disposable:
      disposable.Dispose();
      break;
      }
      
      ……………………
      return default;
      }
      
      public void Dispose()
      {
      DisposeAsync().AsTask().GetAwaiter().GetResult();
      }
      }
      

      為了方便起見,我省略了部分代碼。 這里的關鍵代碼在于: DisposeAsync()方法,它會在內部進行判斷,IServiceScope是否為IAsyncDisposable類型。如果是,則會采用它的IServiceScope的異步釋放方法。

      所以本質上還是回到了官方依賴注入框架中IServiceScope的實現:

      以下代碼位于 DependencyInjection源碼

      internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable, IServiceScopeFactory
      {
      public ValueTask DisposeAsync()
      {
      List<object> toDispose = BeginDispose();
      
      if (toDispose != null)
      {
      try
      {
      for (int i = toDispose.Count - 1; i >= 0; i--)
      {
      object disposable = toDispose[i];
      if (disposable is IAsyncDisposable asyncDisposable)
      {
      ValueTask vt = asyncDisposable.DisposeAsync();
      if (!vt.IsCompletedSuccessfully)
      {
      return Await(i, vt, toDispose);
      }
      
      // If its a IValueTaskSource backed ValueTask,
      // inform it its result has been read so it can reset
      vt.GetAwaiter().GetResult();
      }
      else
      {
      ((IDisposable)disposable).Dispose();
      }
      }
      }
      catch (Exception ex)
      {
      return new ValueTask(Task.FromException(ex));
      }
      }
      
      return default;
      }
      }
      

      可以看出新版本的IServiceScope實現一定是繼承了IAsyncDisposable接口,所以在上面的AspNet Core的代碼里,它一定會調用IServiceScopeDisposeAsync()方法。

      IServiceScope的默認實現在異步釋放時會進行判斷:如果注入的實例為IAsyncDisposable則調用DisposeAsync(),否則判斷是否為IDisposable

      這也解釋了為什么我們在上面同時實現兩個釋放接口,卻只有異步版本的會被調用。

      總結

      在上面的文章中,我們了解到IAsyncDisposable作為.NET異步發展中一個重要的新接口,在應用上會被越來越頻繁的使用,它將逐步完善.NET的異步生態。

      當存在下方的情況時,我們應該優先考慮來使用它:

      • 當內部擁有的資源具有對IAsyncDisposable的實現(比如Utf8JsonWriter等),我們可以采用使用IAsyncDisposable來對他們進行釋放。
      • 當在異步的大環境下,新編寫一個需要釋放資源的類,可以優先考慮使用IAsyncDisposable

      現在.NET的很多類庫都已經同時支持了IDisposableIAsyncDisposable。而從使用者的角度來看,其實調用任何一個釋放方法都能夠達到釋放資源的目的。就好比DbContextSaveChangesSaveChangesAsync

      但是從未來的發展角度來看,IAsyncDisposable會成使用的更加頻繁。因為它應該能夠優雅地處理托管資源,而不必擔心死鎖。

      而對于現在已有代碼中實現了IDisposable的類,如果想要使用IAsyncDisposable。建議您同時實現兩個接口,已保證使用者在使用時,無論調用哪個接口都能達到效果,而達到兼容性的目的。

      類似于下方代碼:

      節選自Stream類的源碼

      public void Dispose() => Close();
      
      public virtual void Close()
      {
      
      Dispose(true);
      GC.SuppressFinalize(this);
      }
      
      public virtual ValueTask DisposeAsync()
      {
      try
      {
      Dispose();
      return default;
      }
      catch (Exception exc)
      {
      return ValueTask.FromException(exc);
      }
      }
      

      最后的最后,希望 點贊,關注,一鍵三連 走一波。

      我們的微信公眾號二維碼

      posted @ 2021-08-31 10:39  句幽  閱讀(676)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产精品一区二区三区污| 不卡视频在线一区二区三区 | 国产一区二区三区色噜噜| 艳妇臀荡乳欲伦69调教视频| 一级片黄色一区二区三区| 国产老妇伦国产熟女老妇高清| 精品国产精品午夜福利| 熟女人妻视频| 精品无码国产不卡在线观看| 精品人妻少妇一区二区三区在线| 西西444www高清大胆| 亚洲国产精品午夜福利| 国产成人亚洲精品狼色在线| 精品午夜福利在线视在亚洲| 看全色黄大黄大色免费久久| 亚洲第一香蕉视频啪啪爽| 久久精品免视看国产成人| 韩国无码AV片午夜福利| 又黄又硬又湿又刺激视频免费| 九九热免费精品在线视频| 一本色道国产在线观看二区| 老熟女重囗味hdxx69| 梓潼县| 成人区人妻精品一区二区| 国产精品有码在线观看| 欧美国产日产一区二区| 国产午夜精品久久一二区| 成人免费xxxxx在线观看| 国产精品高清视亚洲乱码| 亚洲精品国产一二三区| 久久99精品久久久久久9| 久久久久久亚洲精品成人| 免费看无码自慰一区二区| 国产精品一区二区三区色| 欧美熟妇性XXXX欧美熟人多毛| 在线日韩日本国产亚洲| 亚洲激情一区二区三区在线| 国产精欧美一区二区三区| 天天爽夜夜爱| 精品无码老熟妇magnet| 国产老熟女视频一区二区|