单例模式:确保一个类只有一个实例,并提供一个全局访问点。(定义)
概念拆解:
(1)确保一个类只有一个实例
(2)提供一个访问它的全局访问点
个人理解:
一个类不被new,在类里的方法不被重复的new,在多线程调用实例时,确保只有一个实例在运行。
生活中的例子:
一个国家只有一个总统。
简单的单例模式代码:
using UnityEngine;
using System.Collections;
/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance;
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
》》在多线程中,需要确保一个实例。(我们可以使用线程锁lock来控制 )
/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance;
// 定义一个标识确保线程同步
private static readonly object locker = new object();
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加 锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
》》多线程的“双重锁定”(目的:为了减少不必要的开销)
/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance;
// 定义一个标识确保线程同步
private static readonly object locker = new object();
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
// 双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
》》简单模拟网站计数功能
public class Singleton
{
private static Singleton instance;//静态实例
private static readonly object locker = new object();//静态锁
public int count = 1;
private Singleton() {//构造函数
while (true)
{
Console.ReadKey();
count += 1;
Console.WriteLine(count.ToString());
}
}
public static Singleton GetInstance()//方法,方法中去实例化类.
{
if (instance == null)
{
lock(locker)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
》》 在某个时候我只需要一个线程去处理事务,不想有多个实例时。例如我们建立一个数据库存取的管理类。类中有许多的方法。
public class DBManager
{
private static DBManager uniqueInstance;//定义一个静态变量来保存类的实例
private static readonly object locker = new object();//定义一个线程锁
private static SQLiteHelp sqliteHelper;//实例化一个数据库连接
#region 全局访问点
private DBManager()
{
string dbPath = Application.StartupPath + "\\Data.thl";
sqliteHelper = new SQLiteHelp("data source=" + dbPath + ";Pooling=true;FailIfMissing=false");
}
/// <summary>
/// 全局访问点
/// </summary>
/// <returns></returns>
public static DBManager GetInstance()
{
if (uniqueInstance == null)
{
lock (locker)
{
if (uniqueInstance == null)
{
uniqueInstance = new DBManager();
}
}
}
return uniqueInstance;
}
#endregion
#region 文件列表操作
/// <summary>
/// 插入路径
/// </summary>
/// <param name="path">路径</param>
/// <returns></returns>
public bool InsertFilePath(string path)
{
string sql = "insert into RunInfoList (FilePath,Status) values ('" + path + "','停止')";
var affectedCount = sqliteHelper.ExecuteNonQuery(sql);
if (affectedCount == 1)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 插入路径
/// </summary>
/// <param name="serverId">游服ID</param>
/// <param name="path">路径</param>
/// <returns></returns>
public bool InsertFilePath(int serverId,string path)
{
string sql = "insert into RunInfoList (ServerId,FilePath,Status) values ('"+serverId+"','" + path + "','停止')";
var affectedCount = sqliteHelper.ExecuteNonQuery(sql);
if (affectedCount == 1)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 删除
/// </summary>
/// <param name="path">路径</param>
/// <returns></returns>
public bool DeleteFilePath(string path)
{
string sql = "delete from RunInfoList where FilePath = '" + path + "'";
var affectedCount = sqliteHelper.ExecuteNonQuery(sql);
if (affectedCount == 1)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 更新最后的备份时间
/// </summary>
/// <param name="path">路径</param>
/// <returns></returns>
public bool UpdateTime(string path)
{
string sql = "update RunInfoList set LastBackupTime=datetime('now') where FilePath = '" + path + "'";
var affectedCount = sqliteHelper.ExecuteNonQuery(sql);
if (affectedCount == 1)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 获取文件列表
/// </summary>
/// <returns></returns>
public List<FileListInfo> GetFileList()
{
string sql = "select * from RunInfoList";
var reader = sqliteHelper.ReturnDataReader(sql);
List<FileListInfo> fileList = new List<FileListInfo>();
while (reader.Read())
{
FileListInfo data = new FileListInfo();
if (!Convert.IsDBNull(reader["ServerId"]))
{
data.ServerId = Convert.ToInt32(reader["ServerId"]);
}
if (!Convert.IsDBNull(reader["FilePath"]))
{
data.FilePath = reader["FilePath"].ToString();
}
if (!Convert.IsDBNull(reader["Status"]))
{
data.Status = reader["Status"].ToString();
}
if (!Convert.IsDBNull(reader["LastBackupTime"]))
{
data.LastBackTime = reader["LastBackupTime"].ToString();
}
fileList.Add(data);
}
return fileList;
}
/// <summary>
/// 更新ServerId
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool UpdateServerId(string path,string serverid)
{
string sql = "update RunInfoList set ServerId='"+serverid+"' where FilePath = '" + path + "'";
var affectedCount = sqliteHelper.ExecuteNonQuery(sql);
if (affectedCount == 1)
{
return true;
}
else
{
return false;
}
}
#endregion
}
》》说了单例模式的一些概念和代码,重要的是我们要怎么应用在实际的开发中?以下是我在博客园找到的关于单例的运用场景。
- Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
- windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
- 网站的计数器,一般也是采用单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
- 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。