LoveCherry

技术无极限

博客园 首页 新随笔 联系 订阅 管理
  199 Posts :: 0 Stories :: 3047 Comments :: 656 Trackbacks

 

无废话C#设计模式之二:Singleton

 

意图

 

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

 

场景

 

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

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

 

示例代码

  

using System;

using System.Collections.Generic;

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();

     

        }

 

        static void EnterPlayer(object playerName)

        {

            LoadBalanceServer lbs = LoadBalanceServer.GetLoadBalanceServer();

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

        }

    }

 

    class LoadBalanceServer

    {

        private const int SERVER_COUNT = 3;

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

 

        private static volatile LoadBalanceServer lbs;

        private static object syncLock = new object();

 

        private LoadBalanceServer()

        {

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

            {

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

            }

        }

 

        public static LoadBalanceServer GetLoadBalanceServer()

        {

            if (lbs == null)

            {

                lock (syncLock)

                {

                    if (lbs == null)

                    {

                        Thread.Sleep(100);

                        lbs = new LoadBalanceServer();

                    }

                }

            }

            return lbs;

        }

 

        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);

        }

    }

}

 

 

代码执行结果如下图:

 

 

代码说明

 

l         LoadBalanceServer类实现了Singleton模式,也就是说无论在什么情况下,只会有一个LoadBalanceServer类的实例出现。

l         LobbyServer类表示大厅服务,用户进入大厅后和大厅服务进行服务,在这里我们仅仅在大厅服务里面保存了用户列表。

l         Singleton模式有很多实现方式,在这里使用的是双重锁定方式。对于C#来说,可能使用静态初始化方式是最简洁的,这里就不演示了。

l         LoadBalanceServer类的GetLobbyServer()方法负责返回一个压力最小的LobbyServer对象。

l         实例化LoadBalanceServer的时候Sleep了线程,目的是模拟高并发的情况,在正式代码中没有必要这样做。

 

何时采用

 

l         从代码角度来说,当你希望类只有一个实例的时候。

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

 

实现要点

 

l         一个Singleton类,它能确保自身的实例是唯一的。

 

注意事项

 

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

2
0
(请您对文章做出评价)
« 上一篇:一步一步学Linq to sql(十):分层构架的例子
» 下一篇:(原创)无废话C#设计模式之三:Abstract Factory
posted on 2007-10-05 13:36 lovecherry 阅读(7704) 评论(24)  编辑 收藏 所属分类: Design Pattern

Feedback

写得不错!只是LobbyServer有点瑕疵

public List<string> PlayerList
{
get { return playerList; }
}

private string serverName;
public string ServerName
{
get { return serverName; }
}

这两个属性只需只读即可。

  回复  引用    

#2楼[楼主] 2007-10-10 08:13 lovecherry      
@路过
的确是这样的,我比较懒,顺手就用了VS封装字段的功能。。。

  回复  引用  查看    

#3楼 2007-10-16 22:35 base[未注册用户]
用Singleton模式实现的类,跟将类的所有成员跟方法声明成Static的类是否有区别?
  回复  引用    

#4楼[楼主] 2007-10-17 09:35 lovecherry      
@base
没有实例和一个实例的区别

  回复  引用  查看    

#5楼 2007-10-17 16:51 ouyangyc[未注册用户]
一个Singleton类和一个静态类有什么不一样?
  回复  引用    

#6楼 2007-10-18 09:47 codefan[未注册用户]
构造函数为什么私有化?
  回复  引用    

#7楼 2007-10-18 14:15 arpu[未注册用户]
哥哥编译有错误
  回复  引用    

#8楼 2007-10-19 17:07 Hollydsj      
public static LoadBalanceServer GetLoadBalanceServer()
{
if (lbs == null)
{
lock (syncLock)
{
if (lbs == null)
{
Thread.Sleep(100);
lbs = new LoadBalanceServer();
}
}
}
return lbs;
}

@lovecherry 对于用双重锁定方式实现单件模式,网上褒贬不一。不知道您是怎么理解双重锁定和下面的实现方法,谁优谁劣?

public static LoadBalanceServer GetLoadBalanceServer()
{
lock (syncLock)
{
if (lbs == null)
{
Thread.Sleep(100);
lbs = new LoadBalanceServer();
}
}
return lbs;
}
还请指教!!

  回复  引用  查看    

#9楼 2007-10-25 14:35 abblly[未注册用户]
public LoadBalanceServer()

{

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

{

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

}

}
要改成private的吧?

  回复  引用    

#10楼[楼主] 2007-10-25 14:51 lovecherry      
@abblly
谢谢,你这样的阅读者使得BLOG的价值大增

  回复  引用  查看    

#11楼 2008-01-02 20:06 gctren[未注册用户]
你好! 我想请教一个场景, 如果说多少服务器是用配置来文件来控制的话, 如何做到我能方便的修改配置文件就能添加服务器, 而不需要进行重启系统. 这种情况下如何能很好的利用单键模式呢?
  回复  引用    

单例的实现感觉不怎么好,找了一些资料,还请赐教

http://www.yoda.arachsys.com/csharp/singleton.html

  回复  引用    

@Hollydsj
为性能考虑
if判断比lock耗时要少,lock需要内核对象.
因为只有一次需要lock进入然后实例化,以后都不会需要了,所以外部套个if速度会快些.

  回复  引用    

程序编译执行,有时OK,有时会在ShowServerInfo()中出现异常,偶用的是VS2008.说ls.PlayerList已改变,无法枚举
  回复  引用    

private static object syncLock = new object();
public void EnterPlayer(string playerName)
{
lock (syncLock)
{
playerList.Add(playerName);
}
}
改成这样就没有问题了。

  回复  引用    

提示无法枚举的错误,把这段
foreach (string player in ls.PlayerList)

{

Console.WriteLine(player);

}
改成这个就可以了
for (int i=0;i<ls.PlayerList.Count;i++)

{

Console.WriteLine(ls.PlayerList[i]);

}

  回复  引用    

#17楼 2009-08-09 14:22 yeefa[未注册用户]
我想问一下"syncLock"在这里起到一个什么作用呢?
  回复  引用    

#18楼 2009-08-09 14:29 yeefa[未注册用户]
public static LoadBalanceServer GetLoadBalanceServer()
{
if (lbs == null)
{
lock (syncLock)
{
if (lbs == null)
{
Thread.Sleep(100);
lbs = new LoadBalanceServer();
}
}
}
return lbs;
}

这里前面已经有了if (lbs == null)
,为什么还要在lock下面增加if (lbs == null)

  回复  引用    

private static volatile LoadBalanceServer lbs;
这个为什么加上volatile关键字?

  回复  引用