日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何正确的使用单例模式

發(fā)布時(shí)間:2025/3/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何正确的使用单例模式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  在最近的一個(gè)項(xiàng)目里面發(fā)現(xiàn)好多同事喜歡這樣運(yùn)用單例模式,樣例代碼如下

public class Demo {   public static Demo Instance{     get { return new Demo(); }}  public string GetUserId(){     return "001";}  public string GetUserName(){     return "tauruswu";} }

在調(diào)用這個(gè)類的時(shí)候,是這樣操作的

var id = Demo.Instance.GetUserId(); var name = Demo.Instance.GetUserName();

粗略一看,可能覺得沒有問題,最開始我也是這樣,看別人都這么寫,我也就這么寫,其實(shí)這個(gè)時(shí)候你的直覺已經(jīng)明顯的欺騙你了,各位看官再仔細(xì)看看Demo類里面的靜態(tài)屬性Instance以及我們調(diào)用的方式,有沒有看出什么端倪來?

  很顯然,上面的調(diào)用方法已經(jīng)違背了單例模式的宗旨,或者可以說是披著單例模式的外衣,卻不做單例模式該做的事情。單例模式的解釋是:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。那么我們應(yīng)該如何正確的使用單例模式了?

  何為單例模式

  再回頭看上面解釋單例模式的話,第一句話說“保證一個(gè)類僅有一個(gè)實(shí)例”,好,那么我們?cè)鯓幽軌虮WC一個(gè)類僅有一個(gè)實(shí)例了,幸好在C#里面,提供了私有構(gòu)造器,我們?cè)趧?chuàng)建一個(gè)類的時(shí)候,往往會(huì)在類的構(gòu)造函數(shù)里面初始化一些對(duì)象,這里的構(gòu)造器是公開的,如下面

public Demo(){// to do }

那么很顯然,私有構(gòu)造器就是private的,如下面

private Demo(){// to do }

一旦有了私有構(gòu)造器,那么這個(gè)類就會(huì)阻止類外面的代碼創(chuàng)建實(shí)例,不相信我們就來嘗試一下。還是用上面的Demo類

public class Demo {   private Demo(){ // to do } }

然后再外面去實(shí)例化它,看截圖

?這樣一來,你應(yīng)該明白了私有構(gòu)造器的作用了吧。

  解釋單例模式的前半句話上面說的很清楚了,然后在看看后半句“并提供一個(gè)訪問它的全局訪問點(diǎn)”,也就是說向外部提供訪問該實(shí)例的方法或者屬性,怎么寫?我們將開篇的例子稍作修改

public class Demo {private Demo(){     // to do   }  private static Demo _instance;  public static Demo Instance{     get{if (_instance == null)
      {
        Console.WriteLine(string.Format("線程{0}在{1}時(shí)刻發(fā)現(xiàn)Instance為null", Thread.CurrentThread.Name,DateTime.Now));_instance
= new Demo();
      }
return _instance;}} }

調(diào)用方式與開篇的一樣,這個(gè)時(shí)候你在單步調(diào)試進(jìn)去,看看發(fā)現(xiàn)了什么。到這里,我們因該能正確的理解單例模式以及如何使用單例模式了。

  多線程環(huán)境下莫名其妙的錯(cuò)誤

  上面的例子在單線程環(huán)境下可以正常的運(yùn)轉(zhuǎn),如果換做是多線程環(huán)境下,它還能正確的運(yùn)轉(zhuǎn)嗎?

  我們來做這樣一個(gè)實(shí)驗(yàn):1. 在一個(gè)程序啟動(dòng)時(shí)創(chuàng)建兩個(gè)線程,線程A與線程B

             2. 線程A與線程B分別調(diào)用Demo類

如果僅憑自覺的話,我們肯定會(huì)覺得只有一個(gè)線程來創(chuàng)建Demo的實(shí)例,那么事實(shí)是不是這樣了?Demo類還是上面的那個(gè)類,未作任何修改。然后在另一個(gè)類中啟動(dòng)兩個(gè)線程,分別調(diào)用類Demo

public class Invoke{public void Run(){Thread t1 = new Thread(new ThreadStart(fun1));t1.Name = "AAA";t1.Start();Thread t2 = new Thread(new ThreadStart(fun2));t2.Name = "BBB";t2.Start();}private void fun1(){while (true){Demo1.Instance.GetUserId();Thread.Sleep(1);}}private void fun2(){while (true){Demo1.Instance.GetUserId();Thread.Sleep(1);}}}

最后我們?cè)诳刂婆_(tái)程序里面運(yùn)行調(diào)用類Singleton,看效果圖

哥,你目瞪口呆了吧?怎么會(huì)有這樣的結(jié)果?事實(shí)證明上面的寫法在多線程環(huán)境里面會(huì)出問題的,那么我們?cè)撛趺礃尤バ薷乃?#xff0c;讓它能在多線程環(huán)境下正確的運(yùn)行。

  如何修正在多線程環(huán)境下的bug

  這里我們會(huì)用到著名的雙檢鎖技術(shù),英文名就是“Double-Check Locking”,它是線程同步機(jī)制中的一種,它背后的思路是,如果對(duì)象已經(jīng)構(gòu)造好,就不需要線程同步,另外如果調(diào)用如上面提到的屬性“Instance”的線程A發(fā)現(xiàn)對(duì)象沒有創(chuàng)建好,就會(huì)獲取一個(gè)線程同步鎖來確保只有一個(gè)線程構(gòu)造單例對(duì)象,基于這,我們將Demo類再稍微調(diào)整下

public class Demo1{private Demo1(){// to do }private static Demo1 _lock = new Demo1();private static Demo1 _instance;public static Demo1 Instance{get{if (_instance != null) return _instance;Monitor.Enter(_lock);if (_instance == null){Console.WriteLine(string.Format("線程{0}在{1}時(shí)刻發(fā)現(xiàn)Instance為null", Thread.CurrentThread.Name, DateTime.Now));_instance = new Demo1();}Monitor.Exit(_lock);return _instance;}}}

然后在調(diào)用類中再啟動(dòng)多兩個(gè)線程CCC,DDD,再次啟動(dòng)程序

這次的結(jié)果表明只有一個(gè)線程創(chuàng)建了Demo類的實(shí)例了。其實(shí)上面的寫法不是很嚴(yán)謹(jǐn)?shù)?#xff0c;就是當(dāng)私有構(gòu)造器未執(zhí)行完,其他的線程已經(jīng)發(fā)現(xiàn)Instance不為null了,不過這個(gè)問題很難模擬出來。未了解決這種問題,那么就要用到Interlocked.Exchange() 這個(gè)方法。

  還有其他方式創(chuàng)建單例嗎

  除了雙檢索技術(shù),還有其他方式實(shí)現(xiàn)單例模式嗎?答案是肯定的。先來看些下面這種方式

public class Demo2{private static Demo2 _demo2 = new Demo2();private Demo2(){Console.WriteLine(string.Format("線程{0}在{1}時(shí)刻執(zhí)行私有構(gòu)造函數(shù)", Thread.CurrentThread.Name, DateTime.Now));}public static Demo2 Instance{get { return _demo2; }}}

?

在看下執(zhí)行結(jié)果圖

?

那么它的原理是什么了?這里涉及到類型構(gòu)造器了,由于當(dāng)代碼首次訪問類的一個(gè)成員時(shí),CLR 會(huì)自動(dòng)調(diào)用一個(gè)類型的類構(gòu)造器,所以當(dāng)有一個(gè)線程訪問屬性Instance的時(shí)候,CLR會(huì)自動(dòng)調(diào)用類構(gòu)造器,從而創(chuàng)建這個(gè)對(duì)象的實(shí)例。

  總結(jié)

  這個(gè)話題已經(jīng)被寫亂了,如果我之前不仔細(xì)看項(xiàng)目里面的代碼,我也不會(huì)發(fā)現(xiàn)這個(gè)問題,有些時(shí)候總是會(huì)被感覺所欺騙,所以最好的方法就是自己動(dòng)手親自實(shí)踐一番,無非就是幾個(gè)小時(shí)的事情而已。那么你看完這篇文章之后有沒有什么感想了?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/wucj/p/3157294.html

總結(jié)

以上是生活随笔為你收集整理的如何正确的使用单例模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。