C#设计模式之一:Singleton模式

 参考:http://hi.baidu.com/gw820522/blog/item/8fec02cb19379b1cbe09e6ff.html

使用情况:

保证一个类只有一个实例,并提供访问它的全局访问点。

举例如下:

我们现在要做一个网络游戏的服务端程序,需要考虑怎么样才能承载大量的用户。在做WEB程序的时候有各种负载均衡的方案,不管是通过硬件实现还是软件实现,基本的思想就是有一个统一的入口,然后由它来分配用户到各个服务器上去。

需要考虑的问题是,即使在多线程的并发状态下,用户只能通过一个唯一的入口来分配,由此引入了Singleton模式来实现这个唯一的入口。 

以下代码在控制台情况下,经过编译 

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;


namespace SingletonExample

{

    class Program

    {

        static void Main(string[] args)

        {

            ParameterizedThreadStart ts = new ParameterizedThreadStart(EnterPlayer);

            for (int i = 0; i < 20; i++)

            {

                Thread t = new Thread(ts);

                t.Start("player" + i);

            }

            LoadBalanceServer.GetLoadBalanceServer().ShowServerInfo();

            Console.ReadLine();

        }


        static void EnterPlayer(object playerName)

        {

            LoadBalanceServer lbs = LoadBalanceServer.GetLoadBalanceServer();

            lbs.GetLobbyServer().EnterPlayer(playerName.ToString());

        }

    }


    //实现了SingleTon模式。负责入口,任何时候,都只有唯一实例

    class LoadBalanceServer

    {

        private const int SERVER_COUNT = 3;

        private List<LobbyServer> serverList = new List<LobbyServer>();


        //任何时候,都有唯一的实例

        private static volatile LoadBalanceServer lbs;  //volatile 关键字表示字段可能被多个并发执行线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。

        private static object syncLock = new object();

        public LoadBalanceServer()

        {

            for (int i = 0; i < SERVER_COUNT; i++)

            {

                serverList.Add(new LobbyServer("LobbyServer" + i));

            }

        }


        //获得唯一实例

        public static LoadBalanceServer GetLoadBalanceServer()

        {

            if (lbs == null)

            {

                lock (syncLock) //lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

                {

                    if (lbs == null)

                    {

                        Thread.Sleep(100);

                        lbs = new LoadBalanceServer();

                    }

                }

            }

            return lbs;

        }


        //负责返回一个压力最小的LobbyServer对象

        public LobbyServer GetLobbyServer()

        {

            LobbyServer ls = serverList[0];

            for (int i = 1; i < SERVER_COUNT; i++)

            {

                if (serverList[i].PlayerList.Count < ls.PlayerList.Count)

                    ls = serverList[i];

            }

            return ls;

        }

        public void ShowServerInfo()

        {

            foreach (LobbyServer ls in serverList)

            {

                Console.WriteLine("=================" + ls.ServerName + "=================");

                foreach (string player in ls.PlayerList)

                {

                    Console.WriteLine(player);

                }

            }

        }

    }


    //服务大厅类

    class LobbyServer

    {

        private List<string> playerList = new List<string>();

        public List<string> PlayerList

        {

            get { return playerList; }

        }

        private string serverName;

        public string ServerName

        {

            get { return serverName; }

        }

        public LobbyServer(string serverName)

        {

            this.serverName = serverName;

        }

        public void EnterPlayer(string playerName)

        {

            playerList.Add(playerName);

        }

    }

}

    

代码说明
  LoadBalanceServer类实现了Singleton模式,也就是说无论在什么情况下,只会有一个LoadBalanceServer类的实例出现。
  LobbyServer类表示大厅服务,用户进入大厅后和大厅服务进行服务,在这里我们仅仅在大厅服务里面保存了用户列表。
   Singleton模式有很多实现方式,在这里使用的是双重锁定方式。对于C#来说,可能使用静态初始化方式是最简洁的,这里就不演示了。

  LoadBalanceServer类的GetLobbyServer()方法负责返回一个压力最小的LobbyServer对象。
  实例化LoadBalanceServer的时候Sleep了线程,目的是模拟高并发的情况,在正式代码中没有必要这样做。

  何时采用
  从代码角度来说,当你希望类只有一个实例的时候。
  从应用角度来说,你希望有一个总管来负责某一件事情。并且这件事情的分配只能有一个人进行,如果有多个人进行肯定会弄乱。比如创建处理流水号如果有两个地方在创建的话是不是就会重复了呢?

  实现要点
  一个Singleton类,它能确保自身的实例是唯一的。
  注意事项

  不要滥用Singleton模式,只有非一个实例不可的情况下才考虑引入Singleton。否则,程序的可扩展性可能会受到限制。

 

学习知识点:

1、volatile用法

2、lock用法。同时注意,lock也可以用监视器实现,即Monitor,(Enter和Exit)他们是等效的。

     使用 lock 关键字通常比直接使用 Monitor 类更可取,一方面是因为 lock 更简洁,另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。这是通过 finally 关键字来实现的,无论是否引发异常它都执行关联的代码块。 

 3、模式本身

4、多线程调度 

 

我的实例

 

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ThreadStart start = new ThreadStart(Add);    //启动一个新线程,做Add操作
            Thread newThread = new Thread(start);
            newThread.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ThreadStart start = new ThreadStart(Add);   //再启动一个新线程,做Add操作
            Thread newThread = new Thread(start);
            newThread.Start();
        }                                                                          反复点击button1 和button2 ,你会发现,MessageBox.show的值是交替增加的,说明用的是同一个类实例

        private void Add()
        {
            int temp=Singleton.GetSingletonInstance().Count++;
            MessageBox.Show(temp.ToString());
            Thread.Sleep(100000);
        }
    }

    public class Singleton
    {
        private static volatile Singleton SingletonInstance;

        private int count;                                           //Effective C#:尽量使用属性,少使用数据成员

        public int Count
        {
            get { return count; }
            set { count = value; }
        }
        private static object syncLock = new object();

        public static Singleton GetSingletonInstance()
        {
            if (SingletonInstance == null)
            {
                lock (syncLock)//防止两个线程同时初始化实例
                {
                    SingletonInstance=new Singleton();
                }
            }
            return SingletonInstance;
        }
    }
}

 

 


 

 

你可能感兴趣的:(Singleton)