<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          小心使用 Task.Run 續(xù)篇

          共 2675字,需瀏覽 6分鐘

           ·

          2020-12-09 00:38


          關(guān)于前兩天發(fā)布的文章:為什么要小心使用 Task.Run,對文中演示的示例到底會不會導(dǎo)致內(nèi)存泄露,給很多人帶來了疑惑。這點我必須向大家道歉,是我對導(dǎo)致內(nèi)存泄漏的原因沒描述和解釋清楚,也沒用實際的示例證實,是我的錯。

          但是,文中示例演示的?Task.Run?捕獲類成員的情況,確實會有內(nèi)存泄漏的風(fēng)險,我將在本文演示給大家看。

          如果一個對象(或數(shù)據(jù))不需要再使用了,但依然還一直占據(jù)內(nèi)存空間,則視為內(nèi)存泄漏。這一點大家觀點是一致的吧,那如何來檢測對象有沒有被回收呢?

          我們知道,在 C# 中,實例對象被釋放回收,必然會執(zhí)行析構(gòu)函數(shù)。所以我們可以對一個類重寫其析構(gòu)函數(shù),如果該類的實例對象使用完后,強制執(zhí)行 GC 回收,其析構(gòu)函數(shù)依然不被執(zhí)行,則說明 GC 沒有回收該對象。若 GC 后面一直不回收這個對象,則說明存在內(nèi)存泄漏。

          手動強制執(zhí)行?GC 回收的代碼如下:

          GC.Collect();
          GC.WaitForPendingFinalizers();
          GC.Collect();

          這三句代碼可以確保 GC 把所有能搜索到的可回收對象清理干凈。注意:不推薦在生產(chǎn)環(huán)境這樣寫。

          我們還是用?為什么要小心使用 Task.Run?這篇文章用到的示例,只是為了測試稍加修改了一下:

          class?Program
          {
          static?void?Main(string[] args)
          {
          Test();

          // 對不需要再使用的資源強制回收
          GC.Collect();
          GC.WaitForPendingFinalizers();
          GC.Collect();

          // 程序?;?/span>
          while (true)
          {
          Thread.Sleep(100);
          }
          }

          static?void?Test()
          {
          var myClass = new MyClass();
          myClass.Foo();
          // 到這,myClass對象不需要再使用了
          }
          }

          public?class?MyClass
          {
          private?int _id;
          private List<string> _list;

          public Task Foo()
          {
          return Task.Run(() =>
          {
          Console.WriteLine($"Task.Run is executing with ID {_id}");
          Thread.Sleep(100); // 模板耗時操作
          });
          }

          ~MyClass()
          {
          Console.WriteLine("MyClass instance has been colleted.");
          }
          }

          我們在?myClass?對象使用完后,手動強制執(zhí)行 GC 回收,運行結(jié)果如下:

          我們看到?MyClass?的析構(gòu)函數(shù)一直沒有執(zhí)行,也就意味著它的實例一直沒有被回收。

          現(xiàn)在我們修改?MyClass?類的?Foo?方法,改用本地(局部)變量試一試:

          ...
          public Task Foo()
          {
          var localId = _id;
          return Task.Run(() =>
          {
          Console.WriteLine($"Task.Run is executing with ID {localId}");
          });
          }
          ...

          再運行看看效果:

          這次我們可以看到,MyClass?的析構(gòu)函數(shù)執(zhí)行了,說明實例對象被回收了。

          前后唯一區(qū)別是,前者在?Task.Run?的匿名方法中捕獲了類的成員,而后者使用了本地變量。前者出現(xiàn)了內(nèi)存泄漏,后者避免了內(nèi)存泄漏。

          所以,在?Task.Run?的匿名方法中捕獲類的成員,確實有可能導(dǎo)致內(nèi)存泄漏(注意是有可能而不是一定)。

          那背后的原因是什么呢?我在上一篇文章是這樣解釋的:

          私有成員?_id?被?Task.Run?的匿名方法捕獲使用,進而導(dǎo)致?MyClass?實例被引用。當外部使用完?MyClass?實例時,本該由 GC 回收的時候卻發(fā)現(xiàn)它還被其它資源引用著,所以 GC 認為該實例不應(yīng)該被回收,也就可能永遠失去了被回收的機會。

          這個解釋有很大的問題,至少給廣大讀者帶來了兩大疑惑:

          1. 由于值類型是拷貝的方式賦值,所以捕獲的本地變量和類成員指向的是各自的值,對本地變量的捕獲不會影響到整個類。但如果把?_id?改為引用類型(如 String),那兩者指向的就是同一個對象值,那是不是意味著即便使用本地變量也還是無法避免內(nèi)存泄漏的問題?

          2. GC 第一次回收時發(fā)現(xiàn)?myClass?實例存在被捕獲的成員,則認為它不應(yīng)該被回收。那當?Task.Run?執(zhí)行完后, 被捕獲的成員也使用完了,GC 再次搜索時不就可以回收?myClass?對象嗎?只是晚了一些時間回收而已嘛。

          感謝善于思考提出疑惑的讀者們,為你們點贊。

          這兩大疑惑該如何解釋?后半部分我還沒寫完,大家可以先思考一下,我將在下一篇給大家解惑,望大家見諒。當然,我的解釋也不一定會是對的,希望大家?guī)е鴳岩傻膽B(tài)度和批判性思維來看我的文章,也請大家分享自己的理解和觀點。


          往期精彩回顧




          【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程?★★★

          .NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃

          【.NET Core微服務(wù)實戰(zhàn)-統(tǒng)一身份認證】開篇及目錄索引

          Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了

          10個小技巧助您寫出高性能的ASP.NET Core代碼

          用abp vNext快速開發(fā)Quartz.NET定時任務(wù)管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實現(xiàn)作業(yè)調(diào)度

          現(xiàn)身說法:實際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

          關(guān)于C#異步編程你應(yīng)該了解的幾點建議

          C#異步編程看這篇就夠了


          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩字幕 | 国产综合久久7777777 | 国产在线综合免费视频 | 欧美综合播放网站在线观看 | 强开小嫩苞一区二区三区网站 |