C語言封裝線程與日誌類

折騰了兩天,查半天資料還是不如自己動動手。如題這是兩個很常見的問題,雖然很多語言都封裝了線程,但是讓自己寫一個還是比較麻煩的,這兒做一個簡單的demo,大家可以去完善。

WriLog.h

#pragma once

#include <queue>
#include <time.h>

#ifdef WRILOGDLL
#define WRILOGAPI __declspec(dllexport)
#else
#define WRILOGAPI __declspec(dllimport)
#endif

//日誌工具包
#define MAX_MSG_TEXT	1024*10
#define MAX_MSG_TITLE	1024

//日誌消息結構體
struct LogMessage
{
	char msgText[MAX_MSG_TEXT];	//日誌消息信息
	char msgType[256];		//消息類型
	int level;			//消息級別
};

//消息隊列,使用到了stl的隊列
class CLogQueue
{
private:
	std::queue<LogMessage *> m_queue;
	CRITICAL_SECTION cs;
public:
	CLogQueue();
	~CLogQueue();

	void Lock();
    void UnLock();
	LogMessage *pop();
	void push(LogMessage *new_value);
	BOOL empty() const;
	int  size();
};

//簡單的線程類 有時間再完整的封裝這個類
class WRILOGAPI CMyThread
{
public:
	HANDLE hThread;	
	DWORD threadID;

public:
	BOOL	m_terminated;	//是否終止
	BOOL	m_finished;	//是否結束
	BOOL	m_running;	//是否運行

public:
	CMyThread(void);
	~CMyThread(void);

	 enum ThreadState
	 {
		 Runing		=	0x00, //運行狀態
		 Finished	=	0x01, //完成
	 };

	 virtual void Execute(void)=0;	//線程不能實例化,必須要繼承實現該函數

	//獲取線程狀態
	ThreadState	GetThreadState();

	//線程操作
	BOOL	Start();
	BOOL	Suspend();
    	BOOL	Resume();
	void	Stop();

	void	Terminate();	//結束線程
};

class WRILOGAPI CWriteLog: public CMyThread
{
private:
	char m_filePath[256];	//路徑
	char m_fileName[256];	//文件名
	int	 m_level;	//日誌級別,這個一般從配置裏面讀取

	char m_curDate[256];	//當前日期
public:
	CWriteLog(const char *pLogName, const char *path);
	~CWriteLog(void);

	enum LogLevel{
		Error	=	0x00,	//錯誤 級別最高,默認輸出
		Warning =	0x01,	//警告
		Hint	=	0x02,	//提示
		Normal	=	0x03,	//一般
		Debug	=	0x04,	//調試
	};

	CLogQueue m_logList;

	void SetLevel(int level=0);
	void AddLog(const char *msgText, const char *msgType, int level = 0);
	void WriteLog(LogMessage *msg);

	void Execute(void); 
};

//線程函數
DWORD WINAPI ThreadProc(void *pMyThread);
//這兒可以再封裝一個函數,客戶端不用手動實例化日誌類,直接調用這個函數寫日誌
//extern "C" void WRILOGAPI WriteLog(const char *str, int level = 0);


簡單的頭文件大概就是這樣,實現部分如下:

LogUtil.cpp

#include "StdAfx.h"
#include "WriLog.h"

///////////////////////////CLogQueue//////////////////////////

CLogQueue::CLogQueue()
{
	InitializeCriticalSection(&cs);
}

CLogQueue::~CLogQueue()
{
	DeleteCriticalSection(&cs);
}

void CLogQueue::Lock()
{
	EnterCriticalSection(&cs);
}

void CLogQueue::UnLock()
{
	LeaveCriticalSection(&cs);
}

LogMessage *CLogQueue::pop()
{
	LogMessage *res = NULL;
	if(!m_queue.empty())
	{
		Lock();
		res=m_queue.front();
		m_queue.pop();
		UnLock();
	}
	return res;
}

void CLogQueue::push(LogMessage *new_value)
{
	Lock();
	m_queue.push(new_value);
	UnLock();
}

BOOL CLogQueue::empty() const
{
	return m_queue.empty();
}

int  CLogQueue::size()
{
	return m_queue.size();
}


/////////////////////////////CMyThread///////////////////////////////////

CMyThread::CMyThread(void)
{
	m_terminated = false;
	m_finished = false;
	m_running = false;
	hThread = ::CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)ThreadProc, this, 0, &threadID); 
}

CMyThread::~CMyThread(void)
{
}

void CMyThread::Terminate()
{
	m_terminated = true;
}

DWORD WINAPI ThreadProc(void *pMyThread)
{
	CMyThread *thread = (CMyThread *)pMyThread;
	
	//判斷是否終止
	if(!thread->m_terminated)
	{
		thread->Execute();
	}

	//線程執行完畢後這兒需要根據設置來判斷是否進行清理工作,對於線程類來說,線程執行完了類也就可以釋放了,這個隨你喜歡

	return 0;
}


WriLog.cpp

// WriLog.cpp : 定義 DLL 應用程序的導出函數。
//

#include "stdafx.h"
#include "direct.h"
#include "WriLog.h"

CWriteLog::CWriteLog(const char *pLogName, const char *path)
{ 
	memset(m_filePath, 0, 256);
	memset(m_fileName, 0, 256);
	memset(m_curDate, 0, 256);
	m_level = 0;

	if(pLogName)
		strncpy(m_fileName, pLogName, strlen(pLogName));
	if(path)
		strncpy(m_filePath, path, strlen(path));

	time_t timep;  
    	struct tm *p;  
    	time(&timep);  
    	p =localtime(&timep); 
	strftime(m_curDate, 256, "%Y-%m-%d", p);  
}


CWriteLog::~CWriteLog(void)
{
	while(m_logList.size()>0)
	{
		LogMessage *msg;
		msg = m_logList.pop();
		delete msg;
	}
}

void CWriteLog::SetLevel(int level)
{
	m_level = level;
}

void CWriteLog::AddLog(const char *msgText, const char *msgType, int level)
{
	LogMessage *msg;
	msg = new LogMessage();
	memset(msg, 0, sizeof(LogMessage));
	if(msgText && strlen(msgText)>0)
		strncpy(msg->msgText, msgText, strlen(msgText));
	else{
		delete msg;
		return;
	}
	if(msgType && strlen(msgType)>0)
		strncpy(msg->msgType, msgType, strlen(msgType));
	msg->level = level;

	m_logList.push(msg);
}

void CWriteLog::WriteLog(LogMessage *msg)
{
	if(NULL == msg)
		return;

	//如果當前日誌級別比配置的大,則不寫入
	if(msg->level > m_level)
		return;

	char fullPath[MAX_MSG_TITLE];	//完整路徑 D:\test\filename_error_2016-5-11.log
	memset(fullPath, 0, MAX_MSG_TITLE);
	if(strcmp(msg->msgType, "")>0){
		strcat(fullPath, m_filePath);
		strcat(fullPath, "\\");
		strcat(fullPath, m_fileName);
		strcat(fullPath, "_");
		strcat(fullPath, msg->msgType);
		strcat(fullPath, "_");
		strcat(fullPath, m_curDate);
		strcat(fullPath, ".log");
	}
	else{
		strcat(fullPath, m_filePath);
		strcat(fullPath, "\\");
		strcat(fullPath, m_fileName);
		strcat(fullPath, "_");
		strcat(fullPath, m_curDate);
		strcat(fullPath, ".log");
	}

	char title[256];
	memset(title, 0, 256);
	time_t timep;  
    struct tm *p;  
    time(&timep);  
    p =localtime(&timep); 
	strftime(title, 256, "%X.", p);  

	FILE *pFile;
	if(pFile = fopen(fullPath, "a+"))
	{

		fputs(title, pFile);			//寫入標題,這兒僅寫入時間
		fprintf(pFile,"%d:", threadID);		//寫入線程ID
		fputs(msg->msgText, pFile);		//寫入消息
		fprintf(pFile,"\n");			//換行

		fclose(pFile); 
	}
}

void CWriteLog::Execute(void)
{
	LogMessage *msg;
	msg = m_logList.pop();
	while(!m_terminated)
	{
		if(NULL != msg)
		{
			WriteLog(msg);
			delete msg;
		}
		//這兒應該用事件處理waitforsingle..,避免浪費cpu
		Sleep(10);
		msg = m_logList.pop();
	}
}


一個簡單的封裝就完成了,下面是demo:

CWriteLog *m_pLog;
 m_pLog = new CWriteLog("監控日誌", "c:\\test\\log");
 m_pLog->SetLevel(5);

這樣就可以調用了

m_pLog->AddLog("測試。。", "Debug", CWriteLog::Debug);

最後是釋放:

if(m_pLog)
 {
  //如果線程執行較複雜的程序,這兒有可能不能馬上關掉線程,
  //此時刪除實例可能會卡死或報錯,最好的辦法是使用事件來監聽
  //一旦調用terminate函數則立即退出線程
  m_pLog->Terminate();
  delete m_pLog;
 }


工程在我的資源裏面。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章