Linux学习之出错处理(线程安全的日志类封装)

日志类"CLLog"

头文件(CLLog.h):

View Code
#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):

View Code
#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,指示程序每次读写变量都必须从内存中读取,不要进行缓存(寄存器)。



你可能感兴趣的:(linux)