最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

簡(jiǎn)介

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

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

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

幾種實(shí)現(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;
    }
  }
}

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

二簡(jiǎn)單的線程安全實(shí)現(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;
      }
    }
  }
}

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

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

Note: C#中,同一個(gè)線程是可以對(duì)一個(gè)object進(jìn)行多次上鎖的,但是不同線程之間如果同時(shí)上鎖,就可能會(huì)出現(xiàn)線程等待,或者嚴(yán)重的會(huì)出現(xiàn)死鎖情況。因此,我們?cè)谑褂胠ock時(shí),盡量選擇類中的私有變量上鎖,這樣可以避免上述情況發(fā)生。

三雙重驗(yàn)證的線程安全實(shí)現(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;
    }
  } 
}

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

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

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

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

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

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

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

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

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

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

實(shí)現(xiàn)五是實(shí)現(xiàn)四的包裝。它確保了instance只會(huì)在Instance的get方法里面調(diào)用,且只會(huì)在第一次調(diào)用前初始化。它是實(shí)現(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>來實(shí)現(xiàn)延遲加載,它用最簡(jiǎn)潔的代碼保證了單例的線程安全和延遲加載特性。

性能差異

之前的實(shí)現(xiàn)中,我們都在強(qiáng)調(diào)代碼的線程安全性和延遲加載。然而在實(shí)際使用中,如果你的單例類的初始化不是一個(gè)很耗時(shí)的操作或者初始化順序不會(huì)導(dǎo)致bug,延遲初始化是一個(gè)可有可無的特性,因?yàn)槌跏蓟加玫臅r(shí)間是可以忽略不計(jì)的。

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

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

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

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

總結(jié)

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

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

引用

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

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

相關(guān)文章

  • 簡(jiǎn)單掌握Windows中C#啟動(dòng)外部程序進(jìn)程的方法

    簡(jiǎn)單掌握Windows中C#啟動(dòng)外部程序進(jìn)程的方法

    這篇文章主要介紹了Windows中C#啟動(dòng)外部程序進(jìn)程的方法,例子中同時(shí)包括了進(jìn)程關(guān)閉的方法,需要的朋友可以參考下
    2016-03-03
  • 通俗易懂的C#之反射教程

    通俗易懂的C#之反射教程

    這篇文章主要介紹了通俗易懂的C#之反射教程,本文深入分析了反射內(nèi)部的原理,剖析了內(nèi)部屬性、方法的運(yùn)作,并用通俗易懂的語言闡述,需要的朋友可以參考下
    2015-01-01
  • C#?使用Fluent?API?創(chuàng)建自己的DSL(推薦)

    C#?使用Fluent?API?創(chuàng)建自己的DSL(推薦)

    DSL領(lǐng)域?qū)S谜Z言是描述特定領(lǐng)域問題的語言,聽起來很唬人,其實(shí)不是什么高深的東西,下面通過實(shí)例代碼介紹下C#?使用Fluent?API?創(chuàng)建自己的DSL,感興趣的朋友參考下吧
    2021-12-12
  • C#設(shè)置或驗(yàn)證PDF文本域格式的方法詳解

    C#設(shè)置或驗(yàn)證PDF文本域格式的方法詳解

    PDF中的文本域可以通過設(shè)置不同格式,用于顯示數(shù)字、貨幣、日期、時(shí)間、郵政編碼、電話號(hào)碼和社保號(hào)等等。本文將介紹如何通過C#設(shè)置或驗(yàn)證PDF文本域格式,需要的可以參考一下
    2022-01-01
  • C#實(shí)現(xiàn)二叉排序樹代碼實(shí)例

    C#實(shí)現(xiàn)二叉排序樹代碼實(shí)例

    今天小編就為大家分享一篇關(guān)于C#實(shí)現(xiàn)二叉排序樹代碼實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼

    C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼

    這篇文章介紹了C#通過標(biāo)簽軟件Bartender的ZPL命令打印條碼,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-01-01
  • C# 通過同步和異步實(shí)現(xiàn)優(yōu)化做早餐的時(shí)間

    C# 通過同步和異步實(shí)現(xiàn)優(yōu)化做早餐的時(shí)間

    本文以一個(gè)簡(jiǎn)單的小例子—如何做一頓早餐及如何優(yōu)化做早餐的時(shí)間來讓大家具體了解一下同步和異步方法的區(qū)別,需要的朋友可以參考一下
    2021-12-12
  • 如何使用C# Stopwatch 測(cè)量微秒級(jí)精確度

    如何使用C# Stopwatch 測(cè)量微秒級(jí)精確度

    這篇文章主要介紹了如何使用C# Stopwatch 測(cè)量微秒精確度,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • C#開發(fā)中經(jīng)常用的加密解密方法示例

    C#開發(fā)中經(jīng)常用的加密解密方法示例

    這篇文章主要給大家介紹了關(guān)于C#開發(fā)中經(jīng)常用的加密解密方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 詳解C# 利用反射根據(jù)類名創(chuàng)建類的實(shí)例對(duì)象

    詳解C# 利用反射根據(jù)類名創(chuàng)建類的實(shí)例對(duì)象

    這篇文章主要介紹了詳解C# 利用反射根據(jù)類名創(chuàng)建類的實(shí)例對(duì)象,“反射”其實(shí)就是利用程序集的元數(shù)據(jù)信息,感興趣的小伙伴們可以參考一下。
    2017-03-03

最新評(píng)論

迁安市| 广南县| 永登县| 新乡县| 信丰县| 栖霞市| 孝昌县| 合肥市| 化州市| 远安县| 永清县| 松潘县| 天门市| 高碑店市| 长兴县| 黄冈市| 泊头市| 景宁| 津市市| 铜陵市| 余庆县| 东安县| 乳山市| 凉山| 荆州市| 民和| 辛集市| 龙口市| 青州市| 土默特右旗| 彭水| 长阳| 黄梅县| 临沧市| 长兴县| 丁青县| 鄱阳县| 洛阳市| 楚雄市| 洞口县| 平阴县|