一、前置工作
准备有如图所示的MFC界面,即开关线程的按钮以及显示数据的List Control控件(List Control控件属性中view视图必须更改为report否则不显示任何效果!)。
右键list control控件添加变量m_list
二.编程目标
开启线程后,在线程中收集在整个项目其他活动的发生并放入一个队列(此处全部用开启线程活动代替其他活动)。并设置一个定时器定时刷新队列中的数据,然后显示在list control中。
三.实现
1.在初始化代码处实现以下内容:
// TODO: 在此添加额外的初始化代码
m_LogList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
m_LogList.InsertColumn(m_iIndex++, _T("序号"), LVCFMT_CENTER,60);
m_LogList.InsertColumn(m_iIndex++, _T("时间"), LVCFMT_CENTER, 260);
m_LogList.InsertColumn(m_iIndex++, _T("日志内容"), LVCFMT_CENTER, 290); //列
InitializeCriticalSection(&g_cs); //初始化临界区
m_heventClose = CreateEvent(NULL, TRUE, FALSE, NULL); //事件CreateEvent
SetTimer(0, 100, NULL); //设置定时器,100ms刷新一次。并在OnTime函数中实现和控件的交互
在.h文件实现的变量:
int m_iIndex; //顶部的列号,在设置列的时候用上
HANDLE m_heventClose; //定义事件变量
在.cpp文件实现的全局变量和结构体:
typedef struct
{
std::string strLog; //日志内容
std::string strTime; //日志时间
}TLog;
CRITICAL_SECTION g_cs; //线程锁
std::queue<TLog> g_LogQue; //日志队列
- 双击开启线程按钮,该按钮的事件函数:
ResetEvent(m_heventClose); //重置事件,将重新开启线程
_beginthreadex(NULL, 0, ThreadFun, this, 0, NULL); //开启线程
双击关闭线程按钮,该按钮事件函数:
SetEvent(m_heventClose); //触发事件,则线程停止
3.开启线程之后,线程函数ThreadFun(void* pParam):
unsigned int __stdcall ThreadFun(void* pParam)
{
CCcpp7241Dlg* pdlg = (CCcpp7241Dlg*)pParam;
while (true)
{
if ( WAIT_OBJECT_0 == WaitForSingleObject(pdlg->m_heventClose, 1000) ) //当触发事件后停止线程,没有触发则线程一直运行
{
return 0;
}
EnterCriticalSection(&g_cs);
TLog log;
log.strLog = "开启线程";
log.strTime = CT2A( CTime::GetCurrentTime().Format("%Y年%m月%d日 %X").GetString() );
g_LogQue.push(log);
LeaveCriticalSection(&g_cs);
}
EnterCriticalSection(&g_cs);
TLog log;
log.strLog = "关闭线程";
log.strTime = CT2A(CTime::GetCurrentTime().Format("%Y年%m月%d日 %X").GetString()); //获取系统的当前时间
g_LogQue.push(log); //将获取的时间和事件日志内容放入队列中
LeaveCriticalSection(&g_cs);
return 0;
}
4.在初始化部分的代码中已经setTimer开启了定时器,在OnTimer函数中将刷新的队列展示在list control中:
void CCcpp7241Dlg::OnTimer(UINT_PTR nIDEvent)
{
CString str;
TLog log;
EnterCriticalSection(&g_cs); //由于对队列操作是不受线程保护的,所以需要加锁
if ( g_LogQue.size() > 0 ) //判断是否队列为空,为空时不执行下列代码,即无新事件发生时不显示数据
{
int cnt = m_LogList.GetItemCount(); //获取当前行数
str.Format(_T("%d"), cnt);
m_LogList.InsertItem(cnt, str); //设置下一行的序号
log = g_LogQue.front();
g_LogQue.pop();
str = log.strTime.c_str(); //String类内容转换为CString
m_LogList.SetItemText(cnt, 1, str); //行列中设置值
str = log.strLog.c_str();
m_LogList.SetItemText(cnt, 2, str); //行列中设置值
}
LeaveCriticalSection(&g_cs);
CDialogEx::OnTimer(nIDEvent);
}
因为只有一个定时器使用,所以没有使用switch结构。
5.结果图