的run代码_小心使用 Task.Run 续篇
關(guān)于前兩天發(fā)布的文章:為什么要小心使用 Task.Run,對文中演示的示例到底會不會導(dǎo)致內(nèi)存泄露,給很多人帶來了疑惑。這點(diǎn)我必須向大家道歉,是我對導(dǎo)致內(nèi)存泄漏的原因沒描述和解釋清楚,也沒用實(shí)際的示例證實(shí),是我的錯(cuò)。
但是,文中示例演示的 Task.Run 捕獲類成員的情況,確實(shí)會有內(nèi)存泄漏的風(fēng)險(xiǎn),我將在本文演示給大家看。
如果一個(gè)對象(或數(shù)據(jù))不需要再使用了,但依然還一直占據(jù)內(nèi)存空間,則視為內(nèi)存泄漏。這一點(diǎn)大家觀點(diǎn)是一致的吧,那如何來檢測對象有沒有被回收呢?
我們知道,在 C# 中,實(shí)例對象被釋放回收,必然會執(zhí)行析構(gòu)函數(shù)。所以我們可以對一個(gè)類重寫其析構(gòu)函數(shù),如果該類的實(shí)例對象使用完后,強(qiáng)制執(zhí)行 GC 回收,其析構(gòu)函數(shù)依然不被執(zhí)行,則說明 GC 沒有回收該對象。若 GC 后面一直不回收這個(gè)對象,則說明存在內(nèi)存泄漏。
手動強(qiáng)制執(zhí)行 GC 回收的代碼如下:
GC.Collect();GC.WaitForPendingFinalizers();GC.Collect();這三句代碼可以確保 GC 把所有能搜索到的可回收對象清理干凈。注意:不推薦在生產(chǎn)環(huán)境這樣寫。
我們還是用 為什么要小心使用 Task.Run 這篇文章用到的示例,只是為了測試稍加修改了一下:
class?Program{ static?void?Main(string[] args) { Test(); // 對不需要再使用的資源強(qiáng)制回收 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); // 程序保活 while (true) { Thread.Sleep(100); } } static?void?Test() { var myClass = new MyClass(); myClass.Foo(); // 到這,myClass對象不需要再使用了 }}public?class?MyClass{ private?int _id; private List _list; public Task Foo() { return Task.Run(() => { Console.WriteLine($"Task.Run is executing with ID {_id}"); Thread.Sleep(100); // 模板耗時(shí)操作 }); } ~MyClass() { Console.WriteLine("MyClass instance has been colleted."); }}我們在 myClass 對象使用完后,手動強(qiáng)制執(zhí)行 GC 回收,運(yùn)行結(jié)果如下:
我們看到 MyClass 的析構(gòu)函數(shù)一直沒有執(zhí)行,也就意味著它的實(shí)例一直沒有被回收。
現(xiàn)在我們修改 MyClass 類的 Foo 方法,改用本地(局部)變量試一試:
...public Task Foo(){ var localId = _id; return Task.Run(() => { Console.WriteLine($"Task.Run is executing with ID {localId}"); });}...再運(yùn)行看看效果:
這次我們可以看到,MyClass 的析構(gòu)函數(shù)執(zhí)行了,說明實(shí)例對象被回收了。
前后唯一區(qū)別是,前者在 Task.Run 的匿名方法中捕獲了類的成員,而后者使用了本地變量。前者出現(xiàn)了內(nèi)存泄漏,后者避免了內(nèi)存泄漏。
所以,在 Task.Run 的匿名方法中捕獲類的成員,確實(shí)有可能導(dǎo)致內(nèi)存泄漏(注意是有可能而不是一定)。
那背后的原因是什么呢?我在上一篇文章是這樣解釋的:
私有成員 _id 被 Task.Run 的匿名方法捕獲使用,進(jìn)而導(dǎo)致 MyClass 實(shí)例被引用。當(dāng)外部使用完 MyClass 實(shí)例時(shí),本該由 GC 回收的時(shí)候卻發(fā)現(xiàn)它還被其它資源引用著,所以 GC 認(rèn)為該實(shí)例不應(yīng)該被回收,也就可能永遠(yuǎn)失去了被回收的機(jī)會。
這個(gè)解釋有很大的問題,至少給廣大讀者帶來了兩大疑惑:
感謝善于思考提出疑惑的讀者們,為你們點(diǎn)贊。
這兩大疑惑該如何解釋?后半部分我還沒寫完,大家可以先思考一下,我將在下一篇給大家解惑,望大家見諒。當(dāng)然,我的解釋也不一定會是對的,希望大家?guī)е鴳岩傻膽B(tài)度和批判性思維來看我的文章,也請大家分享自己的理解和觀點(diǎn)。
-
精致碼農(nóng)
帶你洞悉編程與架構(gòu)
點(diǎn)頭像關(guān)注,不錯(cuò)過網(wǎng)海相遇的緣分[比心]
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的的run代码_小心使用 Task.Run 续篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML+CSS+JS实现 ❤️制作lo
- 下一篇: java获取500错误_HTTP 500