一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

設(shè)計(jì)模式(2) 單例模式

 悅光陰 2021-07-16
  • 單例模式
  • 線程安全的Singleton
  • 會(huì)破壞Singleton的情況
  • 線程級(jí)Singleton

單例模式是幾個(gè)創(chuàng)建型模式中最獨(dú)立的一個(gè),它的主要目標(biāo)不是根據(jù)客戶程序調(diào)用生成一個(gè)新的實(shí)例,而是控制某個(gè)類型的實(shí)例數(shù)量只有一個(gè)。
GOF對(duì)單例的描述為:
Ensure a class only has one instance, and provide aglobal point of access to.
—Design Patterns : Elements of Reusable Object-Oriented Software

單例模式

單例模式的應(yīng)用場(chǎng)景不必贅述,先來(lái)一個(gè)最簡(jiǎn)單的實(shí)現(xiàn)方式:

public class Singleton
{
    private Singleton() { }
    private static Singleton instance;
    public static Singleton Instance()
    {
        if (instance == null)
        {
            instance = new Singleton();
        }
        return instance;
    }
}

這里采用的是Lazy方式,也可以在靜態(tài)變量被創(chuàng)建的時(shí)候直接初始化實(shí)例。
這段代碼已經(jīng)可以滿足最初Singleton模式的設(shè)計(jì)要求,在大多數(shù)情況下可以很好地工作。但在多線程環(huán)境下這種實(shí)現(xiàn)方式是存在缺陷的,當(dāng)多個(gè)線程幾乎同時(shí)調(diào)用Singleton類的Instance靜態(tài)屬性的時(shí)候,instance成員可能還沒(méi)有被實(shí)例化,因此它被創(chuàng)建了多次,而且最終Singleton類中保存的是最后創(chuàng)建的那個(gè)實(shí)例,各個(gè)線程引用的對(duì)象不同。

線程安全的Singleton

為了保證多線程環(huán)境下instance實(shí)例只有一個(gè),對(duì)代碼進(jìn)行了優(yōu)化:

public class Singleton
{
    private static volatile Singleton instance;
    public static Singleton Instance()
    {
        if (instance == null)
        {
            lock (typeof(Singleton))
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

相比最初的實(shí)現(xiàn),改變的地方有這幾處:

  • instance使用volatile關(guān)鍵字修飾,它表示字段可能被多個(gè)并發(fā)執(zhí)行的線程修改。
  • 在實(shí)例化前l(fā)ock Singleton類型,避免了多個(gè)線程同時(shí)實(shí)例化的問(wèn)題。
  • 第一個(gè)if加在了lock之前,是為了避免每次調(diào)用都鎖定Singleton類型帶來(lái)的效率下降。
  • lock后再次判斷instance是否為空,是因?yàn)樵诟卟l(fā)場(chǎng)景下,在第一個(gè)線程鎖定并實(shí)例化期間,仍然可能會(huì)有別的線程進(jìn)入到第一層if內(nèi),這樣如果不再次判空,就會(huì)重復(fù)實(shí)例化。

會(huì)破壞Singleton的情況

有些情況會(huì)破壞Singleton的封裝,跳過(guò)“只能有一個(gè)實(shí)例”的限制,在實(shí)際應(yīng)用中要注意規(guī)避。

  • 第一種情況就是實(shí)現(xiàn)ICloneable接口或繼承自其相關(guān)的子類,這樣客戶程序借助ICloneable接口就可以跳過(guò)已經(jīng)被隱藏起來(lái)的構(gòu)造函數(shù)

  • 另外通過(guò)二進(jìn)制、Json之類序列化、反序列化的方式也可以產(chǎn)生新的對(duì)象。

線程級(jí)Singleton

前面討論的是線程安全的Singleton實(shí)現(xiàn),但有時(shí)需要的是更細(xì)粒度的Singleton,比如線程級(jí)的Singleton,只要保證在一個(gè)線程內(nèi)只有一個(gè)實(shí)例即可,這就類似Asp.NET Core 自帶的IOC提供的AddScope注冊(cè)方式,可以保證一個(gè)HttpContext內(nèi)只有一個(gè)實(shí)例。

雖然Asp.NET Core提供類似的現(xiàn)成實(shí)現(xiàn),但如果在非Web環(huán)境下也需要線程級(jí)的實(shí)例控制該怎么辦呢? 結(jié)合C#提供的System.ThreadStaticAttribute可以完成

通過(guò)System.ThreadStaticAttribute可以將某個(gè)靜態(tài)變量限定為僅在本線程內(nèi)部是靜態(tài)的。
實(shí)現(xiàn)如下:

public class ThreadSingleton
{
    private ThreadSingleton() { }

    [ThreadStatic] //instance只在當(dāng)前線程內(nèi)為靜態(tài)
    private static ThreadSingleton instance;
    public static ThreadSingleton Instance()
    {
        if (instance == null)
        {
            instance = new ThreadSingleton();
        }
        return instance;
    }
}

這里再不需要線程鎖了,因?yàn)榫€程級(jí)的單例不需要考慮線程安全。
為了驗(yàn)證實(shí)現(xiàn)的準(zhǔn)確性,首先構(gòu)造一個(gè)線程內(nèi)執(zhí)行的目標(biāo)對(duì)象:

class Work
{
    public static IList<int> Log = new List<int>();

    /// <summary>
    /// 每個(gè)線程的執(zhí)行部分
    /// </summary>
    public void Procedure()
    {
        ThreadSingleton s1 = ThreadSingleton.Instance();
        ThreadSingleton s2 = ThreadSingleton.Instance();

        //證明可以正常構(gòu)造實(shí)例
        Assert.IsNotNull(s1);
        Assert.IsNotNull(s2);

        //驗(yàn)證當(dāng)前線程執(zhí)行體內(nèi)兩次獲取的是同一個(gè)實(shí)例
        Assert.AreEqual(s1.GetHashCode(), s2.GetHashCode());

        //記錄當(dāng)前線程所使用對(duì)象的HashCode
        Log.Add(s1.GetHashCode());
    }
}

這個(gè)類會(huì)在每個(gè)線程內(nèi)部執(zhí)行,并驗(yàn)證線程內(nèi)多次獲取的Instance是同一個(gè)實(shí)例,并記錄這個(gè)實(shí)例的HashCode,以便與別的線程實(shí)例對(duì)比。
接下來(lái)開啟多個(gè)線程同時(shí)執(zhí)行Procedure()方法:

[Test]
public void ThreadSingletonTest()
{
    int threadCount = 4;
    Thread[] threads = new Thread[threadCount];  //創(chuàng)建4個(gè)線程
    for (int i = 0; i < threadCount; i++)
    {
        ThreadStart work = new ThreadStart(new Work().Procedure);
        threads[i] = new Thread(work);
    }

    //執(zhí)行線程
    foreach (var thread in threads)
    {
        thread.Start();
    }

    Thread.Sleep(10000);
    Assert.AreEqual(threadCount, Work.Log.Distinct().Count());
}

Work類的靜態(tài)變量Log中記錄了每個(gè)線程中實(shí)例的HashCode,這些HashCode彼此不相同,且與線程的數(shù)量一致,證明每個(gè)線程間的實(shí)例是不相同的。

參考書籍:
王翔著 《設(shè)計(jì)模式——基于C#的工程化實(shí)現(xiàn)及擴(kuò)展》

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    日韩精品成区中文字幕| 伊人久久青草地综合婷婷| 麻豆国产精品一区二区| 中文字幕乱子论一区二区三区| 美日韩一区二区精品系列| 大香蕉大香蕉手机在线视频| 国产免费操美女逼视频| 国产欧美高清精品一区| 经典欧美熟女激情综合网| 少妇肥臀一区二区三区| 91蜜臀精品一区二区三区| 91久久国产福利自产拍| 欧美韩国日本精品在线| av在线免费观看一区二区三区| 精品国产亚洲av成人一区| 日韩精品中文字幕在线视频| 国产精品人妻熟女毛片av久久| 日韩精品一区二区毛片| 精产国品一二三区麻豆| 日本高清一道一二三区四五区| 欧洲亚洲精品自拍偷拍| 二区久久久国产av色| 免费人妻精品一区二区三区久久久| 亚洲精品一区二区三区日韩| 精品亚洲av一区二区三区| 99久久免费中文字幕| 欧美午夜视频免费观看| 亚洲精品伦理熟女国产一区二区| 国产美女精品人人做人人爽| 久久这里只有精品中文字幕| 免费精品一区二区三区| 欧美日韩欧美国产另类| 午夜精品一区免费视频| 日本不卡一区视频欧美| 欧美人妻免费一区二区三区| 亚洲熟女精品一区二区成人| 中文字幕91在线观看| 欧美一区二区不卡专区| 中文日韩精品视频在线| 在线日本不卡一区二区| 最近中文字幕高清中文字幕无|