欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談C#單例模式的實現(xiàn)和性能對比

 更新時間:2017年09月25日 10:13:43   作者:能不能寫滿三篇  
這篇文章主要介紹了淺談C#單例模式的實現(xiàn)和性能對比的相關(guān)資料,詳細的介紹了6種實現(xiàn)方式,需要的朋友可以參考下

簡介

單例指的是只能存在一個實例的類(在C#中,更準確的說法是在每個AppDomain之中只能存在一個實例的類,它是軟件工程中使用最多的幾種模式之一。在第一個使用者創(chuàng)建了這個類的實例之后,其后需要使用這個類的就只能使用之前創(chuàng)建的實例,無法再創(chuàng)建一個新的實例。通常情況下,單例會在第一次被使用時創(chuàng)建。本文會對C#中幾種單例的實現(xiàn)方式進行介紹,并分析它們之間的線程安全性和性能差異。

單例的實現(xiàn)方式有很多種,但從最簡單的實現(xiàn)(非延遲加載,非線程安全,效率低下),到可延遲加載,線程安全,且高效的實現(xiàn),它們都有一些基本的共同點:

  • 單例類都只有一個private的無參構(gòu)造函數(shù)
  • 類聲明為sealed(不是必須的)
  • 類中有一個靜態(tài)變量保存著所創(chuàng)建的實例的引用
  • 單例類會提供一個靜態(tài)方法或?qū)傩詠矸祷貏?chuàng)建的實例的引用(eg.GetInstance)

幾種實現(xiàn)

一非線程安全

//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;
    }
  }
}

這種方法不是線程安全的,會存在兩個線程同時執(zhí)行if (instance == null)并且創(chuàng)建兩個不同的instance,后創(chuàng)建的會替換掉新創(chuàng)建的,導(dǎo)致之前拿到的reference為空。

二簡單的線程安全實現(xiàn)

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;
      }
    }
  }
}

相比較于實現(xiàn)一,這個版本加上了一個對instance的鎖,在調(diào)用instance之前要先對padlock上鎖,這樣就避免了實現(xiàn)一中的線程沖突,該實現(xiàn)自始至終只會創(chuàng)建一個instance了。但是,由于每次調(diào)用Instance都會使用到鎖,而調(diào)用鎖的開銷較大,這個實現(xiàn)會有一定的性能損失。

注意這里我們使用的是新建一個private的object實例padlock來實現(xiàn)鎖操作,而不是直接對Singleton進行上鎖。直接對類型上鎖會出現(xiàn)潛在的風(fēng)險,因為這個類型是public的,所以理論上它會在任何code里調(diào)用,直接對它上鎖會導(dǎo)致性能問題,甚至?xí)霈F(xiàn)死鎖情況。

Note: C#中,同一個線程是可以對一個object進行多次上鎖的,但是不同線程之間如果同時上鎖,就可能會出現(xiàn)線程等待,或者嚴重的會出現(xiàn)死鎖情況。因此,我們在使用lock時,盡量選擇類中的私有變量上鎖,這樣可以避免上述情況發(fā)生。

三雙重驗證的線程安全實現(xiàn)

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;
    }
  } 
}

在保證線程安全的同時,這個實現(xiàn)還避免了每次調(diào)用Instance都進行l(wèi)ock操作,這會節(jié)約一定的時間。

但是,這種實現(xiàn)也有它的缺點:

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

2程序員在自己實現(xiàn)時很容易出錯。如果對這個模式的代碼進行自己的修改,要倍加小心,因為double check的邏輯較為復(fù)雜,很容易出現(xiàn)思考不周而出錯的情況。

四不用鎖的線程安全實現(xiàn)

public sealed class Singleton
{
  //在Singleton第一次被調(diào)用時會執(zhí)行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;
    }
  }
}

這個實現(xiàn)很簡單,并沒有用到鎖,但是它仍然是線程安全的。這里使用了一個static,readonly的Singleton實例,它會在Singleton第一次被調(diào)用的時候新建一個instance,這里新建時候的線程安全保障是由.NET直接控制的,我們可以認為它是一個原子操作,并且在一個AppDomaing中它只會被創(chuàng)建一次。

這種實現(xiàn)也有一些缺點:

1instance被創(chuàng)建的時機不明,任何對Singleton的調(diào)用都會提前創(chuàng)建instance
2static構(gòu)造函數(shù)的循環(huán)調(diào)用。如有A,B兩個類,A的靜態(tài)構(gòu)造函數(shù)中調(diào)用了B,而B的靜態(tài)構(gòu)造函數(shù)中又調(diào)用了A,這兩個就會形成一個循環(huán)調(diào)用,嚴重的會導(dǎo)致程序崩潰。
3我們需要手動添加Singleton的靜態(tài)構(gòu)造函數(shù)來確保Singleton類型不會被自動加上beforefieldinit這個Attribute,以此來確保instance會在第一次調(diào)用Singleton時才被創(chuàng)建。
4readonly的屬性無法在運行時改變,如果我們需要在程序運行時dispose這個instance再重新創(chuàng)建一個新的instance,這種實現(xiàn)方法就無法滿足。

五完全延遲加載實現(xiàn)(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();
  }
}

實現(xiàn)五是實現(xiàn)四的包裝。它確保了instance只會在Instance的get方法里面調(diào)用,且只會在第一次調(diào)用前初始化。它是實現(xiàn)四的確保延遲加載的版本。

六 使用.NET4的Lazy<T>類型

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<T>來實現(xiàn)延遲加載,它用最簡潔的代碼保證了單例的線程安全和延遲加載特性。

性能差異

之前的實現(xiàn)中,我們都在強調(diào)代碼的線程安全性和延遲加載。然而在實際使用中,如果你的單例類的初始化不是一個很耗時的操作或者初始化順序不會導(dǎo)致bug,延遲初始化是一個可有可無的特性,因為初始化所占用的時間是可以忽略不計的。

在實際使用場景中,如果你的單例實例會被頻繁得調(diào)用(如在一個循環(huán)中),那么為了保證線程安全而帶來的性能消耗是更值得關(guān)注的地方。

為了比較這幾種實現(xiàn)的性能,我做了一個小測試,循環(huán)拿這些實現(xiàn)中的單例9億次,每次調(diào)用instance的方法執(zhí)行一個count++操作,每隔一百萬輸出一次,運行環(huán)境是MBP上的Visual Studio for Mac。結(jié)果如下:

線程安全性 延遲加載 測試運行時間(ms)
實現(xiàn)一 15532
實現(xiàn)二 45803
實現(xiàn)三 15953
實現(xiàn)四 不完全 14572
實現(xiàn)五 14295
實現(xiàn)六 22875

測試方法并不嚴謹,但是仍然可以看出,方法二由于每次都需要調(diào)用lock,是最耗時的,幾乎是其他幾個的三倍。排第二的則是使用.NET Lazy類型的實現(xiàn),比其他多了二分之一左右。其余的四個,則沒有明顯區(qū)別。

總結(jié)

總體來說,上面說的多種單例實現(xiàn)方式在現(xiàn)今的計算機性能下差距都不大,除非你需要特別大并發(fā)量的調(diào)用instance,才會需要去考慮鎖的性能問題。

對于一般的開發(fā)者來說,使用方法二或者方法六來實現(xiàn)單例已經(jīng)是足夠好的了,方法四和五則需要對C#運行流程有一個較好的認識,并且實現(xiàn)時需要掌握一定技巧,并且他們節(jié)省的時間仍然是有限的。

引用

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#中decimal保留2位有效小數(shù)的實現(xiàn)方法

    C#中decimal保留2位有效小數(shù)的實現(xiàn)方法

    這篇文章主要介紹了C#中decimal保留2位有效小數(shù)的實現(xiàn)方法,針對decimal變量保留2位有效小數(shù)有多種方法,可以使用Math.Round方法以及ToString先轉(zhuǎn)換為字符串等操作來實現(xiàn)。具體實現(xiàn)方法感興趣的朋友跟隨小編一起看看吧
    2019-10-10
  • C#獲取變更過的DataTable記錄的實現(xiàn)方法

    C#獲取變更過的DataTable記錄的實現(xiàn)方法

    這篇文章主要介紹了C#獲取變更過的DataTable記錄的實現(xiàn)方法,對初學(xué)者很有學(xué)習(xí)借鑒價值,需要的朋友可以參考下
    2014-08-08
  • C#通過XML節(jié)點屬性/屬性值讀取寫入XML操作代碼實例

    C#通過XML節(jié)點屬性/屬性值讀取寫入XML操作代碼實例

    本文主要介紹C#通過XML節(jié)點屬性、屬性值對XML的讀取,寫入操作,大家參考使用吧
    2013-11-11
  • Unity InputFiled TMP屬性和各種監(jiān)聽示例詳解

    Unity InputFiled TMP屬性和各種監(jiān)聽示例詳解

    這篇文章主要為大家介紹了Unity InputFiled TMP屬性和各種監(jiān)聽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • C# 關(guān)于LoadLibrary的疑問詳解

    C# 關(guān)于LoadLibrary的疑問詳解

    這篇文章主要介紹了C# 關(guān)于LoadLibrary的疑問詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • UnityWebRequest前后端交互實現(xiàn)過程解析

    UnityWebRequest前后端交互實現(xiàn)過程解析

    這篇文章主要介紹了UnityWebRequest前后端交互實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 詳解c# 數(shù)組(Array)

    詳解c# 數(shù)組(Array)

    這篇文章主要介紹了c# 數(shù)組的的相關(guān)資料,文中講解非常細致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Unity實現(xiàn)打磚塊游戲

    Unity實現(xiàn)打磚塊游戲

    這篇文章主要為大家詳細介紹了Unity實現(xiàn)打磚塊游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C#中異步Socket通信編程代碼實例

    C#中異步Socket通信編程代碼實例

    這篇文章主要介紹了C#中異步Socket通信編程代碼實例,本文講解了什么是Socket編程的異步是實現(xiàn)、如何實現(xiàn)Socket異步通信并給出了代碼實例,需要的朋友可以參考下
    2015-03-03
  • C# 實現(xiàn)特殊字符快速轉(zhuǎn)碼

    C# 實現(xiàn)特殊字符快速轉(zhuǎn)碼

    這篇文章主要介紹了C# 實現(xiàn)特殊字符快速轉(zhuǎn)碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01

最新評論