首頁 後端開發 C#.Net教程 C#單例模式的實作以及效能比較的實例

C#單例模式的實作以及效能比較的實例

May 16, 2018 pm 05:08 PM
.net

這篇文章主要介紹了淺談C#單例模式的實現和性能對比的相關資料,詳細的介紹了6種實現方式,需要的朋友可以參考下

##簡介

單例指的是只能存在一個實例的類別(在C#中,更準確的說法是在每個AppDomain之中只能存在一個實例的類,它是軟體工程中使用最多的幾種模式之一。單例會在第一次被使用時建立。有很多種,但從最簡單的實現(非延遲加載,非線程安全,效率低下),到可延遲加載,線程安全,且高效的實現,它們都有一些基本的共同點:

單例類別都只有一個private的無參構造函數
  • 類別宣告為sealed(不是必須的)
  • 類別中有一個靜態變數保存所建立的實例的參考
  • 單例類別會提供一個靜態方法或屬性來傳回所建立的實例的參考(eg.GetInstance)
  • 幾種實作

一非執行緒安全性

//Bad code! Do not use!
public sealed class Singleton
{
  private static Singleton instance = null;
  private Singleton()
  {
  }

  public static Singleton instance
  {
    get
    {
      if (instance == null)
      {
        instance = new Singleton();
      }
      return instance;
    }
  }
}
登入後複製
這種方法不是執行緒安全的,會存在兩個執行緒同時執行if (instance == null)並且建立兩個不同的instance,後面建立的會替換掉新建立的,導致之前拿到的reference為空。 ##二簡單的線程安全實作

public sealed class Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      lock (padlock)
      {
        if (instance == null)
        {
          instance = new Singleton();
        }
        return instance;
      }
    }
  }
}
登入後複製

相比較於實作一,這個版本加上了一個對instance的鎖,在呼叫instance之前要先對padlock上鎖,這樣就避免了實現一中的執行緒衝突,該實作自始至終只會創建一個instance了。 ##注意這裡我們使用的是新建一個private的object實例padlock來實現鎖操作,而不是直接對Singleton進行上鎖。它會在任何code裡調用,直接對它上鎖會導致效能問題,甚至會出現死鎖情況。 ,但是不同執行緒之間如果同時上鎖,就可能會出現執行緒等待,或嚴重的會出現死鎖情況。因此,我們在使用lock時,盡量選擇類別中的私有變數上鎖,這樣可以避免上述情況發生。

三雙驗證的執行緒安全實作

public sealed calss Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      if (instance == null)
      {
        lock (padlock)
        {
          if (instance == null)
          {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  } 
}
登入後複製

在保證執行緒安全的同時,這個實作也避免了每次呼叫Instance都進行lock操作,這會節約一定的時間。

但是,這種實作也有它的缺點:

1無法在Java中運作。 (具體原因可以見原文,這邊沒怎麼理解)

2程式設計師在自己實作時很容易出錯。如果對這個模式的程式碼進行自己的修改,要倍加小心,因為double check的邏輯較為複雜,很容易出現思考不周而出錯的情況。

四不用鎖的執行緒安全實作

public sealed class Singleton
{
  //在Singleton第一次被调用时会执行instance的初始化
  private static readonly Singleton instance = new Singleton();

  //Explicit static consturctor to tell C# compiler 
  //not to mark type as beforefieldinit
  static Singleton()
  {
  }

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return instance;
    }
  }
}
登入後複製

這個實作很簡單,並沒有用到鎖,但是它仍然是執行緒安全的。這裡使用了一個static,readonly的Singleton實例,它會在Singleton第一次被呼叫的時候新建一個instance,這裡新建時候的線程安全保障是由.NET直接控制的,我們可以認為它是一個原子操作,並且在一個AppDomaing中它只會被創建一次。

這種實作也有一些缺點:

1instance被創建的時機不明,任何對Singleton的呼叫都會事先建立instance2static建構子的循環調用。如有A,B兩個類,A的靜態建構子中調用了B,而B的靜態建構函數中又調用了A,這兩個就會形成一個循環調用,嚴重的會導致程式崩潰。 3我們需要手動加入Singleton的靜態建構子來確保Singleton型別不會被自動加上beforefieldinit這個Attribute,以此來確保instance會在第一次呼叫Singleton時才被建立。

4readonly的屬性無法在執行時改變,如果我們需要在程式執行時dispose這個instance再重新建立一個新的instance,這種實作方法就無法滿足。

五完全延遲載入實作(fully lazy instantiation)

public sealed class Singleton
{
  private Singleton()
  {
  }

  public static Singleton Instance 
  {
    get
    {
      return Nested.instance;
    }
  }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton instance = new Singleton();
  }
}
登入後複製

實現五是實現四的包裝。它確保了instance只會在Instance的get方法裡面調用,並且只會在第一次調用前初始化。它是實現四的確保延遲載入的版本。


六使用.NET4的Lazy類型

public sealed class Singleton
{
  private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

  public static Singleton Instance 
  {
    get 
    {
      return lazy.Value;
    }
  }

  private Singleton()
  {
  }
}
登入後複製

.NET4或以上的版本支援Lazy來實現延遲加載,它用最簡潔的程式碼保證了單例的線程安全和延遲載入特性。

效能差異

之前的實作中,我們都在強調程式碼的執行緒安全性和延遲載入。然而在實際使用中,如果你的單例類別的初始化不是一個很耗時的操作或初始化順序不會導致bug,延遲初始化是一個可有可無的特性,因為初始化所佔用的時間是可以忽略不計的。

在實際使用場景中,如果你的單例實例會被頻繁地呼叫(如在一個循環中),那麼為了確保執行緒安全而帶來的效能消耗是更值得關注的地方。

為了比較這幾種實現的效能,我做了一個小測試,循環拿這些實作中的單例9億次,每次呼叫instance的方法執行一個count++操作,每隔一百萬輸出一次,運作環境是MBP上的Visual Studio for Mac。結果如下:


執行緒安全性 延遲載入 測試運行時間(ms)
實作一 #是 15532
實作二 45803
實作三 15953
實作四 不完全 14572
實作五 #14295
實作六 22875

測試方法並不嚴謹,但仍然可以看出,方法二由於每次都需要呼叫lock,是最耗時的,幾乎是其他幾個的三倍。排第二的則是使用.NET Lazy類型的實現,比其他多了二分之一左右。其餘的四個,則沒有明顯差異。

總結

整體來說,上面說的多種單例實作方式在現今的電腦效能下差距都不大,除非你需要特別大並發量的呼叫instance,才會需要去考慮鎖定的效能問題。

對於一般的開發者來說,使用方法二或方法六來實現單例已經是足夠好的了,方法四和五則需要對C#運行流程有一個較好的認識,並且實現時需要掌握一定技巧,並且他們節省的時間仍然是有限的。

引用

本文大部分是翻譯自Implementing the Singleton Pattern in C#,加上了一部分自己的理解。這是我搜尋static readonly field initializer vs static constructor initialization時看到的,在這裡對兩位作者表示感謝。

以上是C#單例模式的實作以及效能比較的實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

分享幾個.NET開源的AI和LLM相關專案框架 分享幾個.NET開源的AI和LLM相關專案框架 May 06, 2024 pm 04:43 PM

當今人工智慧(AI)技術的發展如火如荼,它們在各個領域都展現了巨大的潛力和影響力。今天大姚給大家分享4個.NET開源的AI模型LLM相關的專案框架,希望能為大家提供一些參考。 https://github.com/YSGStudyHards/DotNetGuide/blob/main/docs/DotNet/DotNetProjectPicks.mdSemanticKernelSemanticKernel是一種開源的軟體開發工具包(SDK),旨在將大型語言模型(LLM)如OpenAI、Azure

C#的就業前景如何 C#的就業前景如何 Oct 19, 2023 am 11:02 AM

無論您是初學者還是有經驗的專業人士,掌握C#將為您的職業發展鋪平道路。

面向開發人員的.NET效能最佳化技術 面向開發人員的.NET效能最佳化技術 Sep 12, 2023 am 10:43 AM

如果你是.NET開發者,你必須意識到在交付高品質軟體方面,優化功能和效能的重要性。透過熟練使用提供的資源並減少網站載入時間,你不僅為使用者創造了愉快的體驗,還能減少基礎設施成本。

Java框架與.NET框架的效能差異 Java框架與.NET框架的效能差異 Jun 03, 2024 am 09:19 AM

在高並發請求處理方面,.NETASP.NETCoreWebAPI效能優於JavaSpringMVC,原因包括:AOT提前編譯,減少啟動時間;更精細的記憶體管理,由開發人員負責分配和釋放物件記憶體。

高級C#.NET教程:ACE您的下一次高級開發人員面試 高級C#.NET教程:ACE您的下一次高級開發人員面試 Apr 08, 2025 am 12:06 AM

C#高級開發者面試需要掌握異步編程、LINQ、.NET框架內部工作原理等核心知識。 1.異步編程通過async和await簡化操作,提升應用響應性。 2.LINQ以SQL風格操作數據,需注意性能。 3..NET框架的CLR管理內存,垃圾回收需謹慎使用。

C#.NET面試問題和答案:提高您的專業知識 C#.NET面試問題和答案:提高您的專業知識 Apr 07, 2025 am 12:01 AM

C#.NET面試問題和答案包括基礎知識、核心概念和高級用法。 1)基礎知識:C#是微軟開發的面向對象語言,主要用於.NET框架。 2)核心概念:委託和事件允許動態綁定方法,LINQ提供強大查詢功能。 3)高級用法:異步編程提高響應性,表達式樹用於動態代碼構建。

C#.NET:探索核心概念和編程基礎知識 C#.NET:探索核心概念和編程基礎知識 Apr 10, 2025 am 09:32 AM

C#是一種現代、面向對象的編程語言,由微軟開發並作為.NET框架的一部分。 1.C#支持面向對象編程(OOP),包括封裝、繼承和多態。 2.C#中的異步編程通過async和await關鍵字實現,提高應用的響應性。 3.使用LINQ可以簡潔地處理數據集合。 4.常見錯誤包括空引用異常和索引超出範圍異常,調試技巧包括使用調試器和異常處理。 5.性能優化包括使用StringBuilder和避免不必要的裝箱和拆箱。

.NET中的C#代碼:探索編程過程 .NET中的C#代碼:探索編程過程 Apr 12, 2025 am 12:02 AM

C#在.NET中的編程過程包括以下步驟:1)編寫C#代碼,2)編譯為中間語言(IL),3)由.NET運行時(CLR)執行。 C#在.NET中的優勢在於其現代化語法、強大的類型系統和與.NET框架的緊密集成,適用於從桌面應用到Web服務的各種開發場景。

See all articles