折騰了兩天,查半天資料還是不如自己動動手。如題這是兩個很常見的問題,雖然很多語言都封裝了線程,但是讓自己寫一個還是比較麻煩的,這兒做一個簡單的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;
}
工程在我的資源裏面。