zidate

多线程编程(二)

多线程程序设计成功的关键:
(1) 各线程的数据要分离开来,避免使用全局变量。
(2) 不要在线程之间共享GDI对象
(3) 确定你知道你的线程状态,不要径自结束程序而不等待它们的结束。
(4) 让主线程处理用户界面。
二.关于Wait…()函数
DWORD WaitForSingleObject(
    HANDLE hHandle;
    DWORD dwMilliseconds);
参数:
hHandle---等待对象的handle(代表一个核心对象)
dwMilliseconds—等待的最长时间,时间终了,即使handle尚未称为激发状态,此函数还是要返回,此值可以是0(代表立刻返回),也可以是INFINITE代表无穷等待。
返回值:
如果函数失败,则传回WAIT_FAILED,这时候你可调用GetLastError取得更多信息,此函数的成功有三个因素:
1. 等待的目标(核心对象)变成激发状态,这种情况下返回值将为WAIT_OBJECT_0.
2. 核心对象变成激发状态之前,等待时间终了,这种情况下返回WAIT_TIMEOUT.
3. 如果一个拥有mutex(互斥器)的线程结束前没有释放mutex,则传回WAIT_ABANDONED.
获得一个线程对象的handle之后,WaitForSingleObject要求操作系统让线程1睡觉,直到以下任何一种情况发生:
1. 线程2结束
2. dwMilliseconds时间终了,该值系从函数调用后开始计算。
由于操作系统追踪线程2,所以即使线程2失事或被强迫终止,该函数也能正常工作。
关于该函数的第二个参数,若设定为0,可使你能够检查handle的状态并立刻返回,没有片刻停留,如果handle已经备妥,那么这个函数会成功并传回WAIT_OBJECT_0,否则,这个函数立刻返回并传回WAIT_TIMEOUT.
可被WaitForSingleObject使用的核心对象有两种状态:激发与未激发。Wait函数会在目标变成激发状态时返回。
当线程正在执行时,线程对象处于未激发状态,当线程结束,线程对象就被激发了,因此,任何线程如果等待的是一个线程对象,将会在等待对象结束时被调用,因为当时线程对象自动变成激发状态。
WaitForMultipleObject函数:
DWORD WaitForMultipleObject(
DWORD nCount,
CONST HANDLE *lpHandles,
BOOL bWaitAll,
DWORD dwMillisencods)
参数:
nCount—表示lpHandles所指之handles数组的元素个数,最大容量为MAXIMUM_WAIT_OBJECTS.
lpHandles—指向一个由对象handles所组成的数组,这些handles不需要为相同的类型。
bWaitAll—如果此为true,表示所有的handles都必须激发,此函数才得以返回,否则此函数将在任何一个handle激发时返回。
dwMilliseconds—当该事件长度终了时,即使没有任何handles激发,此函数也会返回,此值可为0,以便测试,亦可指定INFINITE,表示无穷等待。
返回值:
1. 如果因时间终了而返回,则返回值是WAIT_TIMEOUT.
2. 如果bWaitAll是TRUE,那么返回值将是WAIT_OBJECT_0.
3. 如果bWaitAll是FALSE,那么返回值减去WAIT_OBJECT_0,就表示数组中的哪一个handle被激发了。
4. 如果你等待的对象中有任何mutexes,那么返回值可能从WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1.
5. 如果函数失败,它会传回WAIT_FAILED,这时候你可以用GetLastError找出失败的原因。
GetMessage函数等待消息而不是核心对象,一旦你调用GetMessage,除非有一个消息真正进入你的消息队列,否则它不会返回,在此期间,Windows就可以自由地将CPU时间给与其他程序。
如果你正使用WaitSingleObject或WaitForMultipleObjects等待某个对象被激发,你根本没有办法回到主消息循环中去。为解决这个问题,主消息循环必须修改,使它得以同时等待消息或核心对象被激发,必须使用一个MsgWaitForMultipleObjects函数,这个函数非常类似WaitForMultipleObjects,但它会在“对象被激发”或“消息到达队列”时被唤醒而返回。
DWORD MsgWaitForMultipleObjects(
DWORD  nCount,
LPHANDLE  lpHandles,
BOOL  fWaitAll,
DWORD dwMilliseconds,
DWORD dwWakeMask);
参数:
dwWakeMask—欲观察的用户输入消息,可以是:              QS_ALLINPUT,QS_HOTKEY,QS_INPUT,QS_KEY,QS_MOUSE, QS_MOUSEBUTTON,QS_MOUSEMOVE,QS_PAINT,QS_POSTMESSAGE,QS_SENDMESSAGE,QS_TIMER.
返回值:
和WaitForMultipleObjects相比较,MsgWaitForMultipleObjects有一些额外的返回值意义,为了表示“消息到达队列”,返回值将是WAIT_OBJECT_0+nCount。
while(!quit || gNumPrinting > 0)
{
DWORD dwWake;
dwWake = MsgWaitForMultipleObjects(gNumPrintings,gPrintJobs,FALSE,INFINITE,QS_ALLEVENTS);
if(dwWake >= WAIT_OBJECT_0 && dwWake < WAIT_OBJECT_OBJECT_0+gNumPrinting)
{
//处理有信号的核心对象
}
else if(dwWake == WAIT_OBJECT_0 + gNumPrinting)
{
   While(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
 If(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))
{
  if(msg.message == WM_QUIT)
     {
   quit = TRUE;
          exitcode = msg.wParam;
          break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}//end while
}
}// end while
有数种情况是这个循环必须处理而却可能在它第一次设计时容易被忽略的:
1. 在你收到WM_QUIT之后,Windows仍然会传送消息给你,如果你要在收到WM_QUIT之后等待所有线程结束,你必须继续处理你的消息,否则窗口会变得反应迟钝,而且没有重绘能力。
2. 该函数不允许handles数组中有缝隙产生。所以当某个handle被激发了时,你应该在下一次调用该函数之前先把handles数组做个整理,紧压,不要只是把数组中的handle设为NULL。
3. 如果有另一个线程更改了对象数组,而那是你正在等待的,那么你需要一种新方法,可以强迫MsgWaitForMultipleObjects返回,并重新开始,以包含这个新的handle。

posted on 2011-07-22 13:58 Jerry zhang 阅读(206) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理