1. 程式人生 > >【C#設計模式-單例模式】

【C#設計模式-單例模式】

單例模式就是保證在整個應用程式的生命週期中,在任何時刻,被指定的類只有一個例項,併為客戶程式提供一個獲取該例項的全域性訪問點

一:經典模式:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    /// <summary>
    /// 一、經典模式:
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// Singleton的建構函式必須是私有的,以保證客戶程式不會通過new()操作產生一個例項,達到實現單例的目的;
        /// </summary>
        private Singleton(){ }

        /// <summary>
        /// 私有的靜態全域性變數instance來儲存該類的唯一例項;
        /// </summary>
        private static Singleton instance;

        /// <summary>
        /// 提供一個全域性函式訪問獲得該例項,並且在該函式提供控制例項數量的功能,
        /// 即通過if語句判斷instance是否已被例項化,
        /// 如果沒有則可以同new()建立一個例項;
        /// 否則,直接向客戶返回一個例項
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }

        /// <summary>
        /// 測試屬性
        /// </summary>
        public int Age { get; set; }

        public void GetShow()
        {
            this.Age = this.Age + 1;
            Console.WriteLine("我是一個單例物件:Age=" + Age);
        }
    }
}

1)該Singleton的建構函式必須是私有的,以保證客戶程式不會通過new()操作產生一個例項,達到實現單例的目的;

2)因為靜態變數的生命週期跟整個應用程式的生命週期是一樣的,所以可以定義一個私有的靜態全域性變數instance來儲存該類的唯一例項;

3)必須提供一個全域性函式訪問獲得該例項,並且在該函式提供控制例項數量的功能,即通過if語句判斷instance是否已被例項化,如果沒有則可以同new()建立一個例項;否則,直接向客戶返回一個例項。

在這種經典模式下,沒有考慮執行緒併發獲取例項問題,即可能出現兩個執行緒同時獲取instance例項,且此時其為null時,就會出現兩個執行緒分別建立了instance,違反了單例規則。因此,需對上面程式碼修改。

二.多執行緒下的單例模式:

1、Lazy模式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    /// <summary>
    /// 二、多執行緒下的單例模式
  /// 1、Lazy模式
    /// </summary>
    public class Singleton2
    {
        private static Singleton2 instance2;
        private static object _lock = new object();

        private Singleton2()
        {

        }

        public static Singleton2 GetInstance(string thnum)
        {
            //外層的if語句塊,這使得每個執行緒欲獲取例項時不必每次都得加鎖,
            //因為只有例項為空時(即需要建立一個例項),才需加鎖建立,
            //若果已存在一個例項,就直接返回該例項,節省了效能開銷
            if (instance2 == null)
            {
                //Console.WriteLine("object is null" + thnum);
                lock (_lock) 
                {
                    //內層的if語句塊,使用這個語句塊時,先進行加鎖操作,
                    //保證只有一個執行緒可以訪問該語句塊而保證只建立了一個例項
                    if (instance2 == null)
                    {
                        //Console.WriteLine("object not null" + thnum);
                        instance2 = new Singleton2();
                        instance2.Age = 0;
                    }
                }
            }
            return instance2;
        }

        /// <summary>
        /// 測試屬性
        /// </summary>
        public int Age { get; set; }

        public void GetShow(string thnum)
        {
            this.Age = this.Age + 1;
            Console.WriteLine("我是一個單利物件:Age=" + Age+" 執行緒:"+thnum);
        }

    }
}

上述程式碼使用了雙重鎖方式較好地解決了多執行緒下的單例模式實現。

內層的if語句塊,使用這個語句塊時,先進行加鎖操作,保證只有一個執行緒可以訪問該語句塊,進而保證只建立了一個例項。

外層的if語句塊,這使得每個執行緒欲獲取例項時不必每次都得加鎖,因為只有例項為空時(即需要建立一個例項),才需加鎖建立,若果已存在一個例項,就直接返回該例項,節省了效能開銷。

2.餓漢模式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    /// <summary>
    /// 二、多執行緒下的單例模式
    /// 2、餓漢模式
  /// 這種模式的特點是自己主動例項。
    /// </summary>
    public class Singleton3
    {
        /// <summary>
        /// 主動例項
        /// </summary>
        private static readonly Singleton3 instance = new Singleton3();

        private Singleton3()
        {

        }

        public static Singleton3 GetInstance()
        {
            return instance;
        }
    }
}

測試:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("一、經典模式:測試");
            //Singleton s = new Singleton(); Error 編譯器檢測出現錯誤
            Singleton s = Singleton.GetInstance();
            s.GetShow();
            Singleton s1 = Singleton.GetInstance();
            s1.GetShow();
            Singleton s12 = Singleton.GetInstance();
            s12.GetShow();

            Console.WriteLine("二、多執行緒下的單例模式>Lazy模式:測試");
            Thread thr1 = new Thread(x => {
                Singleton2 s2 = Singleton2.GetInstance("thr1");
                s2.GetShow("thr1");
                s2.GetShow("thr1");
            });
            thr1.Start();
            Thread thr2 = new Thread(x => {
                Singleton2 s22 = Singleton2.GetInstance("thr2");
                s22.GetShow("thr2");
            });
            thr2.Start();
            Console.Read();
        }
    }
}