[英]How do we distinguish between managed and unmanaged resources in C#? Is TextFieldParser unmanaged?
[英]Do we have Unmanaged resources in C#?
我和朋友討論了C#中的托管和非托管資源。
根據我的朋友:
1.a)C#中的每個對象都是托管對象,當我們用C#編寫代碼時,沒有像非托管對象或資源那樣的東西。 非托管資源概念僅隨C ++一起提供。
1.b)無論我們在C ++中是托管資源還是非托管資源,我們都需要顯式釋放它。 由於我們在C#中具有自動垃圾收集器,因此無需考慮管理資源。
據我說:
2.a)如果我們沒有非托管資源,那為什么我們需要C#中的finalizer或Dispose方法?
2.b)垃圾收集器僅具有有關已分配內存的信息,而沒有資源狀態的信息。 因此,我們需要使用dispose方法釋放C#中的資源。
我需要幫助來了解上述哪些參數正確,以及有關c#中非托管資源的信息,無論它們是否存在?
提前致謝。
不,如果不使用非托管資源就不可能編寫C#程序。 C#程序不可避免地在100%非托管的操作系統上運行。 如果使用文件,則使用操作系統資源。 網絡連接。 一個線程。 控制台。 Etcetera,所有非常多的非托管資源。
但是,該事實在.NET中隱藏得很好。 框架庫為這些本機對象提供了不錯的包裝器類。 FileStream,Socket,線程,控制台等。 內存也是操作系統資源,垃圾回收器也是它的包裝器。
在所有這些資源中,只有內存資源才真正被自動管理。 他們中的其他人憑借其包裝器類獲得了一定程度的幫助。 它們的終結器是關鍵,它在調用時釋放操作系統資源。 這幾乎是自動的,垃圾回收器注意到包裝類對象不再在任何地方引用,因此它釋放它,然后終結器確保也釋放非托管資源。
通常效果很好,您通常可以在代碼中忽略這些實現細節。 許多程序員都這樣做。
不過,終結器存在問題,它們需要一段時間才能開始運行。 要啟動它們,需要進行垃圾收集,這可能需要花費幾毫秒到幾分鍾的時間。 這是不可預測的,取決於您在代碼中消耗內存的速率。 如果您不大量使用它,則會花費很長時間。
您不能總是承受這么長時間來等待釋放非托管資源。 文件是一個很好的例子。 當您打開一個文件來讀取文件中的數據時,您確實應該在完成讀取后關閉文件。 如果等到終結器完成該工作,則可能會在程序需要一段時間后再次打開文件時冒程序失敗的風險。 您可能已經通過使用FileShare打開文件將自己鎖定了。無,它也會鎖定您自己的代碼。 沒什么大不了的:讀取完成后,您可以調用Close()關閉文件。 為確保關閉,您應該將Close()調用放在finally塊中,以便即使代碼由於異常中止而運行。 實際上,您顯式運行終結器代碼。
更嚴重的情況是非常昂貴的操作系統資源。 位圖就是一個很好的例子,它們可能占用大量非托管內存或數據庫連接,它們的一個池默認情況下僅包含100個。 對於這些,您可以使自己處在這樣的情況下:讓終結器負責釋放資源,因為它花費的時間太長而無法工作。 在終結器可以運行之前,您的程序死於一個異常。 通常很難診斷,因為這往往僅在程序加載時才會發生。 在台式機以外的計算機上,當發生許多事情時,總是很難調試發生的問題。
.NET設計人員意識到了這一需求,並設計了IDisposable接口。 它的Dispose()方法旨在運行通常由終結器運行的代碼,從而為您提供一種顯式釋放資源的方法,而不是等待垃圾收集器處理它的方法。 語言設計者通過在他們的語言中添加using關鍵字來趕上潮流,確保自動調用IDisposable.Dispose()。
如上所述,在代碼中對實現IDisposable的任何對象使用using或Dispose()是可選的,但許多.NET程序員認為這很重要。 主要是因為每個人都開始沒有它的.NET編程,而當他們的程序變大時,遲早會遇到問題。 甚至在不調用Dispose()的類上也有規定,例如MemoryStream。 當類應該實現IDisposable但不能實現IDisposable時,這會引起精神上的痛苦,例如Thread。 或者,當一個類同時實現Dispose和Close時(沒有區別)。 為了進行比較,Java具有相同的考慮因素,但沒有IDisposable。
在.NET中創建的對象是托管代碼,但是您的對象可以保存對非托管資源的引用 。 垃圾收集器(GC)確保不再需要在托管堆上分配的所有內存,然后再清理它們。 但是,盡管垃圾收集器非常擅長確保內存不泄漏,但它對需要釋放的其他資源一無所知。 例如, 垃圾收集器不知道如何使用諸如CoAllocTaskMem之類的API 關閉文件句柄或如何釋放在托管堆之外分配的內存 。
管理這些類型的資源的對象必須確保在不再需要它們時將其釋放。 您可以通過重寫System.Object的Finalize方法來實現此目的,該方法使垃圾收集器知道該對象希望參與其自身的清理(在C#中,您使用C ++析構函數語法〜MyObject,而不是直接重寫該方法)。 如果一個類具有終結器,則在收集該類型的對象之前,垃圾收集器將調用該對象的終結器,並允許其清除它可能持有的所有資源。
該系統的一個問題是,垃圾收集器無法確定性地運行,因此,在最后一次引用該對象之后,您的對象可能很長時間無法完成。 如果您的對象保留了昂貴或稀有的資源(例如數據庫連接),則可能不可接受。 例如,如果您的對象只有10個可用連接中的1個處於打開狀態,則它應盡快釋放該連接,而不是等待垃圾回收器調用finalize方法。
為此,您具有應實現的IDisposable接口。 進一步了解它。
a)C#中的每個對象都是托管對象,當我們用C#編寫代碼時,沒有像非托管對象或資源那樣的東西。 非托管資源概念僅隨C ++一起提供。
這是不正確的。 正如其他人所提到的,我們可以從C#外部(例如COM)獲得非托管資源。
但是,當然可以在C#中擁有“非托管資源”而無需訪問非托管代碼。 從嚴格的垃圾收集角度來看,這些資源可能不是不受管理的,但它們是開發人員必須處理的資源。 以一個線程為例:
class Foo
{
private Thread thread = new Thread(new ThreadStart(DoLotsOfWork));
private AutoResetEvent endThread = new AutoResetEvent(false);
private int sum = 0;
public Foo()
{
thread.Start();
}
public StopThread()
{
endThread.Set();
}
private void DoLotsOfWork()
{
while (!endThread.WaitOne(1000))
{
sum += 1;
}
}
}
static void Main(string[] args)
{
Foo foo = new Foo();
// Additional code...
foo.StopThread();
}
假設其他代碼返回或引發異常。 如果您未明確調用StopThread,則執行DoLotsOfWork的線程將不會結束,並且您的進程可能不會退出。
b)無論我們在C ++中是托管資源還是非托管資源,我們都需要顯式釋放它。 由於我們在C#中具有自動垃圾收集器,因此無需考慮管理資源。
我們絕對必須考慮使用C#管理資源。 正如您所建議的,這就是IDisposable存在的原因。
考慮對上面的代碼進行此修改:
class Foo : IDisposable
{
private bool disposed = false;
private Thread thread = new Thread(new ThreadStart(DoLotsOfWork));
private AutoResetEvent endThread = new AutoResetEvent(false);
private int sum = 0;
public Foo()
{
thread.Start();
}
public StopThread()
{
endThread.Set();
}
public Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void DoLotsOfWork()
{
while (!endThread.WaitOne(1000))
{
sum += 1;
}
}
private void Dispose(bool disposing)
{
if (!disposed && disposing)
{
StopThread();
disposed = true;
}
}
}
static void Main(string[] args)
{
using (Foo foo = new Foo())
{
// Additional code...
}
}
現在我們可以確定,不管附加代碼做什么,由Foo類創建的線程都將在進程退出之前停止。
在.net Framework下創建的所有內容都是Mananged代碼 ,因此內存僅由通過使用.net Framework Manager(按框架)創建的對象使用。
在.net框架之外創建的所有事物都是非托管代碼。
當您要釋放沉重的對象(如需要關閉文件)或使用圖形並且要釋放與其關聯的內存時,可以使用終結器或處置。可以使用此方法。
的確,在CLR中創建的所有對象都是由CLR管理的,因此您無需關心它們。
但是,當您開始使用CLR外部的資源(例如,COM對象或設備上的鎖)時, 您有責任釋放這些資源。 CLR無法為您完成此操作,但是提供了IDisposable
接口,可讓您編寫進行清理的代碼。
我們確實必須處理.NET中的非托管資源。 一個很好的例子是與數據庫的連接。
我們必須顯式關閉該非托管資源。 這是為什么我們在C#中擁有Dispose
一個示例。
擁有非托管資源的對象將其他實體置於某種不可取的狀態(例如,使其無法用於其他目的),直到被告知不再需要該實體為止。 如果在沒有先被告知不再需要它的情況下放棄了它,那么其他實體將處於不希望的狀態。
受管資源是一個實體,它類似地將其他實體置於某種不希望的狀態,直到被告知不再需要它為止,但是如果放棄了對它的所有“故意”引用,它最終將自動清理自身。
長期對象的事件訂閱完全存在於托管代碼中,但是由於在長期對象的生存期內不會自動清除它們,因此應將其視為非托管資源。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.