Linux学习之出错处理(线程安全的日志类封装)
日志类"CLLog"
头文件(CLLog.h):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef CLLog_H
#define CLLog_H
#include <pthread.h>
#include "CLStatus.h"
/*
用于向文件LOG_FILE_NAME中,记录日志信息
*/
class CLLog
{
public:
static CLLog* GetInstance();
static CLStatus WriteLogMsg(const char *pstrMsg, long lErrorCode);
CLStatus WriteLog(const char *pstrMsg, long lErrorCode);
private:
CLLog(const CLLog&);
CLLog& operator=(const CLLog&);
static pthread_mutex_t *InitializeMutex();
private:
CLLog();
~CLLog();
int m_Fd;
pthread_mutex_t *m_pMutexForFile;
static CLLog * volatile m_pLog;
static pthread_mutex_t *m_pMutex;
};
#endif
实现文件(CLLog.cpp):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "CLLog.h"
#define LOG_FILE_NAME "CLLog.txt"
#define MAX_SIZE 265
CLLog* volatile CLLog::m_pLog = 0;
pthread_mutex_t *CLLog::m_pMutex = CLLog::InitializeMutex();
CLLog::CLLog()
{
m_Fd = open(LOG_FILE_NAME, O_RDWR | O_CREAT | O_APPEND, S_IRUSR);
m_pMutexForFile = new pthread_mutex_t;
int r = pthread_mutex_init(m_pMutexForFile, 0);
if(r != 0)
{
delete m_pMutexForFile;
m_pMutexForFile = 0;
}
}
CLLog::~CLLog()
{
if(m_pMutexForFile != 0)
delete m_pMutexForFile;
if(m_Fd != -1)
close(m_Fd);
if(m_pLog != 0)
delete m_pLog;
if(m_pMutex != 0)
delete m_pMutex;
}
CLStatus CLLog::WriteLogMsg(const char *pstrMsg, long lErrorCode)
{
CLLog *pLog = CLLog::GetInstance();
if(pLog == 0)
return CLStatus(-1, 0);
CLStatus s = pLog->WriteLog(pstrMsg, lErrorCode);
if(s.IsSuccess())
return CLStatus(0, 0);
else
return CLStatus(-1, 0);
}
CLStatus CLLog::WriteLog(const char *pstrMsg, long lErrorCode)
{
if(m_Fd == -1)
return CLStatus(-1, 0);
if(m_pMutexForFile == 0)
return CLStatus(-1, 0);
int p = pthread_mutex_lock(m_pMutexForFile);
if(p != 0)
return CLStatus(-1, 0);
ssize_t r = write(m_Fd, pstrMsg, strlen(pstrMsg));
if(r == -1)
{
pthread_mutex_unlock(m_pMutexForFile);
return CLStatus(-1, 0);
}
char buf[MAX_SIZE];
snprintf(buf, MAX_SIZE, " Error code: %ld\r\n", lErrorCode);
r = write(m_Fd, buf, strlen(buf));
if(r == -1)
{
pthread_mutex_unlock(m_pMutexForFile);
return CLStatus(-1, 0);
}
p = pthread_mutex_unlock(m_pMutexForFile);
if(p != 0)
return CLStatus(-1, 0);
return CLStatus(0, 0);
}
pthread_mutex_t *CLLog::InitializeMutex()
{
pthread_mutex_t *p = new pthread_mutex_t;
int r = pthread_mutex_init(p, 0);
if(r != 0)
{
delete p;
return 0;
}
return p;
}
CLLog* CLLog::GetInstance()
{
if(m_pLog == 0)
{
if(m_pMutex == 0)
return 0;
int r = pthread_mutex_lock(m_pMutex);
if(r != 0)
return 0;
if(m_pLog == 0)
{
m_pLog = new CLLog;
}
r = pthread_mutex_unlock(m_pMutex);
if(r != 0)
return 0;
}
return m_pLog;
}
该类采用了"单件模式":m_pLog为业务对象的指针,可能在程序中对其频繁访问,用volatile修饰。
两个pthread_mutex_t * 类型的互斥量:m_pMutexForFile用于同步日志文件的访问;m_pMutex用于保证只创建一个业务对象。
CLLog* volatile CLLog::m_pLog = 0;
pthread_mutex_t *CLLog::m_pMutex = CLLog::InitializeMutex();
静态变量需要初始化,且为了避免链接时的重定义,选择在.cpp中初始化。
在临界区中创建业务对象。所以,在GetInstance()中有两次关于m_pLog的判断:如果取消在临界区中的判断,则依然不能保证业务对象只有一个。
如果取消首个对于m_pLog的判断,则可以保证业务对象的唯一性,但会增加线程间不必要的等待(每个线程在获取业务对象时都要先在临界区外等待)。
本类还有一个特点:就是对文件描述符,业务对象和互斥量的创建完成后,并不检查其有效性,而是在使用时才检查。
volatile变量
一般在多线程中使用的比较多。
Ø例如有一个int x,有两个线程都要对其读写
有些编译器或CPU会将x保存在寄存器中,读的时候直接读取寄存器中的内容,而不是真实的x在内存中的内容
线程1,对x进行加1操作,此时内存中x的值为2
线程2想读x,结果从寄存器中读出1
给变量加上volatile,指示程序每次读写变量都必须从内存中读取,不要进行缓存(寄存器)。