Windows的消息处理与多线程

  • 格式:doc
  • 大小:35.00 KB
  • 文档页数:6

Windows的消息处理与多线程编程
MFC篇 (1)
W INDOWS消息处理 (1)
单位线程是如何处理消息的 (1)
放弃控制 (2)
计时器 (2)
多线程编程 (3)
编写工作者线程函数并启动线程 (3)
主线程如何与工作线程使用全局变量通讯 (3)
工作者线程与主线程通讯发送消息进行联络 (4)
使用事件进行线程同步 (4)
临界段 (5)
用户接口线程 (5)
WIN32 SDK篇 (5)
事件的使用方法 (5)
线程的创建方法 (5)
临界区的使用方法 (6)
MFC篇
Windows消息处理
单位线程是如何处理消息的
Windows的消息处理机制是用如下代码进行消息处理的:
MSG message;
While(::GetMessage(&message,NULL,0,0)){
::TranslateMessage(&message);
::DispatchMessage(&message);
}
当消息到达时,由TranslateMessage进行必要的转换,例如:将WM_KEYDOWN消息转换为包含有ASCII字符的WM_CHAR消息,然后由DispatchMessage进行发送,当处理完成后,DispatchMessage返回.
放弃控制
如果在等待方式下,DispatchMessage必须等待处理完成后才能返回,在此之前将不能处理任何消息,而下面的代码可以做到即使没有消息到达程序的情况下也立即返回
MSG message;
While(::PeekMessage(&message,NULL,0,0,PM_REMOVE)){
::TranslateMessage(&message);
::DispatchMessage(&message);
}
计时器
计时器是不依赖CPU的时钟速度的. 注意的是因为Windows并不是实时的操作系统,所以,如果你指定的周期小于100毫秒的话,计时器事件之间的周期可能不精确.
有了计时器,有时可以替代多线程情况, 例如下面的代码就允许在循环内仍然接收处理消息. 这是一个进度条, 在OnTimer里面改动进度条的显示, 同时可以自定义CANCEL消息, 在OnCancel中将程序终止.
Void CDlg::OnStart()
{
MSG message;
SetTimer(0,100,NULL);
GetDlgItem(IDC_START)->EnableWindow(FALSE); // 使按钮无效
Volatile int nTemp; //使变更不保存在寄存器中, 因为变量如果保存在寄存器中, 在线程的切换过程中可能会出现值的错误.
For (m_nCount=0;m_nCount<nMaxCount;m_nCount++){
For (nTemp=0;nTemp<10000;nTemp++){
………
}
if (::PeekMessage(&message,NULL,0,0,PM_REMOVE)){
::TranslateMessage(&message);
::DispatchMessage(&message);
}
}
CDlg::OnOK(); // 线程结束后关闭对话框
}
多线程编程
进程是拥有自己的内存,文件句柄和其他系统资源的运行程序, 单个进程可以包含独立的执行,叫线程.
Windows提供了两种线程, 工作者(worker)线程和用户界面线程, 用户界面线程通常有窗口,且具有自己的消息循环.工作者线程没有窗口,因此它不需要处理消息.
编写工作者线程函数并启动线程
线程体一般是如何形式:
UINT ThreadProc(LPVOID pParam)
{
return 0;
}
启动线程使用:
CwinThread* pThread =
AfxBeginThread(ThreadProc,GetSafeHwnd(),THREAD_PRIORITY_NORMAL);
主线程如何与工作线程使用全局变量通讯
全局变量通讯是最简单而有效的办法.
例如下面的代码:
UINT ThreadProc(LPVOID pParam)
{
g_nCount = 0;
while(g_nCount<100)
::InterlockedIncrement((long*)&g_nCount);
return 0;
}
InterLockIncrement函数在变量加1期间阻塞其他线程访问该变量. 如果不使用此函数而直接使用:g_nCount++的话, 可能会出现错误.
工作者线程与主线程通讯发送消息进行联络
下面的代码: 当线程完成后发送给父进程消息
UINT ThreadProc(LPVOID pParam)
{
………
::PostMessage((HWND)pParam,WM_THREADFINISHED,0,0);
return 0;
}
使用事件进行线程同步
Cevent类是一个事件类,刚定义时处于"非信号"状态, 可以调用SetEvent()成员函数置它为"信号"状态.
下面的代码: 线程首先等待开始信号, 如果没有置开始信号会一直挂起等待, 同时在运行的过程中等待结束信号, 如果结束信号发生就终止线程.
Cevent g_eventStart,g_eventKill;
UINT ThreadProc(LPVOID pParam)
{
//INFINITE表示等待无限时间
::WaitForSingleObject(g_eventtart,INFINITE);
for (………)
{
……….
If(::WaitForSingleObject(g_eventKill,0)==WAIT_OBJECT_0))
Break;
}
return 0;
}
当启动线程时: g_eventStart.SetEvent();
当终止线程时: g_eventKill.SetEvent();
但在终止线程时如果还没有启动线程,则应该先启动线程再终止它.
注意: 线程如果不正常终止会引起内存泄漏, 例如用关闭进程的方法来强制终止线程,
或使用Win32 TerminateThread函数.
临界段
使用CcriticalSection类可以将临界段句柄包装起来, 例如:
CcriticalSection g_cs;
G_cs.Lock();
G_nCount++;
G_cs.Unlock();
用户接口线程
一般使用用户接口线程是想要得到多个顶级窗口,例如你想允许用户运行程序的多个实例,但是你想让所有的实例都共享内存,IE就是这样的. 你可以从CwinThread来派生一个类,使用AfxBeginThread的重载版本来启动该线程,派生的这个类具有它自己的InitInstance函数,并且具有自己的消息循环.
Win32 SDK篇
事件的使用方法
HANDLE g_hCloseEvent = NULL;
g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_hCloseEvent == NULL)
return FALSE;
设置信号: SetEvent(g_hCloseEvent);
线程的创建方法
线程体的一般形式:
DWORD WINAPI ThreadProc(LPVOID pParam)
{
return 0;
}
创建时:
HANDLE hReceiveThread = NULL;
UINT ThreadID;
hReceiveThread =
CreateThread(NULL,0,ThreadProc,hWnd,0, &ThreadID);
if ( hReceiveThread == NULL )
return FALSE;
// 优先级为普通
SetThreadPriority(hReceiveThread,THREAD_PRIORITY_NORMAL); 临界区的使用方法
CRITICAL_SECTION csRecvRead = {0};
InitializeCriticalSection(&csRecvRead); // 临界区初始化
EnterCriticalSection(&csRecvRead); // 使用临界区变量
p RightBuffer = pRightBuffer + len;
LeaveCriticalSection(&csRecvRead);。