簡體   English   中英

我們在C#中有非托管資源嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM