C++实现多线程安全的单例模式 已测试

来源:互联网 发布:mac版qq不能远程协助 编辑:程序博客网 时间:2024/05/16 12:19

项目中需要在多线程环境下,输出日志到标准输出,以下是实现过程。

首先,我们需要一个锁类,能够自动初始化,并且降低耦合。



  1. /* 
  2.  * locker.
  3.  * 
  4.  * Created on: Apr 14, 2012 
  5.  * Author: joan 
  6.  */ 
  7.   
  8. #ifndef LOCKER_H_ 
  9. #define LOCKER_H_ 
  10.   
  11. #include "../OPTION.h" 
  12.   
  13. class locker 
  14. { 
  15. public: 
  16.     inline locker(){ pthread_mutex_init(&mutex,NULL);} 
  17.     inline ~locker(){ pthread_mutex_destroy(&mutex);} 
  18.     inline void lock(){ pthread_mutex_lock(&mutex);} 
  19.     inline void unlock(){ pthread_mutex_unlock(&mutex);} 
  20. private: 
  21.     pthread_mutex_t mutex; 
  22. }; 
  23.   
  24. #endif /* LOCKER_H_ */
其次,声明日志类,重点是将构造函数私有化,将函数成员和数据成员声明为静态,添加实例指针和全局访问点。

  1. /* 
  2.  * log.
  3.  * 
  4.  * Created on: Apr 8, 2012 
  5.  * Author: joan 
  6.  */ 
  7.   
  8. #ifndef LOG_H_ 
  9. #define LOG_H_ 
  10.   
  11. #include "../OPTION.h" 
  12. #include "locker.h" 
  13.   
  14. /* 
  15.  * this class is responsible for the running log of tinyJSE 
  16.  * there should only exist one instance of tinyLog, 
  17.  * so we use singleton to implement tinyLog 
  18.  */ 
  19. class tinyLog 
  20. { 
  21. public: 
  22.     static tinyLog *GetInstance(); 
  23.     static void WriteLog(const char *FORMAT,...); 
  24. private: 
  25.     tinyLog(); 
  26.     ~tinyLog(); 
  27. private: 
  28.         static tinyLog *log; 
  29. static locker llock; 
  30. }; 
  31. #endif /* LOG_H_ */



然后是日志类的实现,注意全局访问点中使用double check提高性能。


  1. /* 
  2.  * log.cpp 
  3.  * 
  4.  * Created on: Apr 8, 2012 
  5.  * Author: joan 
  6.  */ 
  7.   
  8. #include "../OPTION.h" 
  9. #include "log.h" 
  10.   
  11. tinyLog * tinyLog::log = NULL; 
  12. locker tinyLog::llock; 
  13.   
  14. tinyLog::tinyLog() 
  15. { 
  16. } 
  17.   
  18. tinyLog::~tinyLog() 
  19. { 
  20. } 
  21.   
  22. /* 
  23.  * get the pointer to the only instance of tinyLog 
  24.  * use double check to assure only one instance is created 
  25.  */ 
  26. tinyLog *tinyLog::GetInstance() 
  27. { 
  28.     if(NULL == log) 
  29.     {//double check 
  30.         llock.lock(); 
  31.         if(NULL == log) 
  32.         { 
  33.             log = new tinyLog();       //这里最好换成{ tinyLog * tmpLog=new tinyLog(); log=tmpLog; } 原因请看我另外一篇"C++中多线程Singleton的实现"
  34.         } 
  35.         llock.unlock(); 
  36.     } 
  37.     return log; 
  38. } 
  39.   
  40. /* 
  41.  * Unified handling of the log of tinyJSE 
  42.  */ 
  43. void tinyLog::WriteLog(const char *FORMAT,...) 
  44. { 
  45.     va_list args; 
  46.   
  47.     va_start(args, FORMAT); 
  48.   
  49.     llock.lock(); 
  50.   
  51.     vfprintf(stdout,FORMAT,args); 
  52.   
  53.     llock.unlock(); 
  54.   
  55.     va_end(args); 
  56.   
  57. }
使用该单例:

  1. #define PRINT(FORMAT,args...) tinyLog::GetInstance()->WriteLog(FORMAT,##args)

转: http://blog.csdn.net/joanlynnlove/article/details/7462254

但是自己在用上面的程序中发现, 当同一个线程中调用了两次Locker::Lock()时, 会产生死锁的情况,原因是它用的互斥量是标准的互斥量类型, 并不做任何特殊的错误检查或死锁检测,在同一个线程中调用两次加锁时, 就由于第一次加锁还没有释放,就变成死锁了.  要将mutex的类型属性设置为PTHREAD_MUTEX_RECURSIVE,这种类型的互斥量允许同一线程在互斥量解锁之前对该互斥量进行多次加锁.
(APUE中有说明).

那么lock定义如下:

  1. class Locker
  2. {
  3. public: 
  4.     inline Locker()
  5.         { 
  6.             pthread_mutexattr_t attr;
  7.             pthread_mutexattr_init(&attr);
  8.             int err;
  9.             if(!= (err=pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)))
  10.             {
  11.                 printf("pthread_mutexattr_settype error. mutexattr addr=%x\n",(unsigned)this);
  12.             }
  13.             pthread_mutex_init(&mutex,&attr);
  14.             pthread_mutexattr_destroy(&attr);
  15.         }
  16.     inline ~Locker(){ pthread_mutex_destroy(&mutex);} 
  17.     inline void Lock()
  18.         { 
  19.             
  20.             pthread_mutex_lock(&mutex);
  21.         } 
  22.     inline void Unlock()
  23.         { 
  24.             pthread_mutex_unlock(&mutex);
  25.         } 
  26. private: 
  27.     pthread_mutex_t mutex; 
  28. };
上面Locker类代码经测试在linux下可行.
0 0