关于单例模式的线程安全问题讨论以及加锁时机之我的想法

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于单例模式的线程安全问题讨论以及加锁时机之我的想法相关的知识,希望对你有一定的参考价值。

昨晚和朋友讨论这个单例模式的加锁问题,因为我们是一个老师教出来的嘛,平时关系也好,在一个项目的一个细节上我们出了点不同意见。
就是一个单例,到底要不要单例?
为什么要或者为什么不要?
单例模式是不是线程安全的?
如果不安全,是整个对象锁住还是锁住哪些?


他的想法是:要单例,因为是业务类,需要在多个地方使用到。单例不是线程安全的,整个对象锁住,老师教过,而且static对象创建的时候默认锁住。

我的想法是:要单例,他说的就是我要说的,此外我再补充一点,看下面代码就知道,如果不单例,那用户表可大呢!!!
单例不是线程安全的,我试过不加锁的单例。不要把整个对象锁住。至于static的默认加锁,我没有听说,暂时没有好的测试方案。


接下来我说一下我认为的加锁时机,就拿我最近写的一段代码吧,毕设代码小demo。跟我们讨论的代码是一个风格的。

类设计:

#ifndef SERVICE_H_
#define SERVICE_H_

//处理消息的事件回调方法类型
using MsgHandler = std::function<void(int fd,char* msg)>;

class Service
{
public:

  //单例模式
  static Service* instance(); //为什么要做成单例?你去看看它数据域就知道了。
  //1、数据域大
  //2、数据域应全局共享

  //网络层只需要将数据包直接转入业务层,不需要去拆包
  void Login(int fd,char *msg);
  void Register1(int fd,char *msg);
  void Registers(int fd,char *msg);
  void UpCourse(int fd,char *msg);
  void ChooseCourse(int fd,char *msg);
  void CancelCourse(int fd,char *msg);
  void UpScore(int fd,char *msg);
  void SearchScore(int fd,char *msg);

  //获取消息对应的处理器
  MsgHandler getHandle(int msgid);

private:
  Service();
  //如果这个类对象需要析构,那说明服务器关了,所以这个对象交给操作系统打理了

  //存储消息id和对应的处理方法,用map就够了
  std::map<int,MsgHandler> _msgHanderMap;

  //存储在线用户连接
  std::unordered_map<int,bool> _userConnMap;

  //定义互斥锁
  std::mutex _connMutex;
};

#endif

这个类接下来要扩张,昨晚刚做了一篇API接口安全的博客,嗯。

我个人认为,多线程访问这个对象的时候,只需要把哈希表锁住就好。而操作也仅限于哈希表查找和值修改,是非常快的,锁的粒度要尽可能的小,这是我的观点。
至于每次都把单例对象锁起来,那就算后面再怎么reactor玩的六,这里是串行,整个项目就是串行的,想象一下?想象力不够的买个漏斗多把玩把玩。

以上是关于关于单例模式的线程安全问题讨论以及加锁时机之我的想法的主要内容,如果未能解决你的问题,请参考以下文章

单例设计模式之懒汉式(线程安全)

单例模式的实现——延迟初始化占位类替代双重检验加锁以达到延迟初始化和线程安全的目的。

线程安全的单例模式是否真的安全

单例模式

单例模式详解

双重校验锁实现单例模式(对象单例,线程安全)