折腾了两天,查半天资料还是不如自己动动手。如题这是两个很常见的问题,虽然很多语言都封装了线程,但是让自己写一个还是比较麻烦的,这儿做一个简单的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;
}
工程在我的资源里面。