http://www.cnblogs.com/andyhere/archive/2008/10/20/1314803.html
1.WaitForSingleObject 的用法
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
参数 hHandle 是一个事件的句柄,第二个参数 dwMilliseconds 是时间间隔。如果时间是有信号状态返回 WAIT_OBJECT_0 ,如果时间超过 dwMilliseconds 值但时间事件还是无信号状态则返回 WAIT_TIMEOUT 。
hHandle 可以是下列对象的句柄:
Change notification
Console input
Event
Job
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。在这里举个例子:
先创建一个全局 Event 对象 g_event:
CEvent g_event;
在程序中可以通过调用 CEvent::SetEvent 设置事件为有信号状态。
下面是一个线程函数 MyThreadPro()
UINT CFlushDlg::MyThreadProc( LPVOID pParam )
{
WaitForSingleObject(g_event,INFINITE);
For(;;)
{
………… .
}
return 0;
}
在这个线程函数中只有设置 g_event 为有信号状态时才执行下面的 for 循环,因为 g_event 是全局变量,所以我们可以在别的线程中通过 g_event. SetEvent 控制这个线程。
还有一种用法就是我们可以通过 WaitForSingleObject 函数来间隔的执行一个线程函数的函数体
UINT CFlushDlg::MyThreadProc( LPVOID pParam )
{
while(WaitForSingleObject(g_event,MT_INTERVAL)!=WAIT_OBJECT_0)
{
………………
}
return 0;
}
在这个线程函数中可以可以通过设置 MT_INTERVAL 来控制这个线程的函数体多久执行一次,当事件为无信号状态时函数体隔 MT_INTERVAL 执行一次,当设置事件为有信号状态时,线程就执行完毕了。
2. WaitForSingleObject
当指定的对象处于有信号状态或者等待时间结束的状态时,此函数返回。
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数:
hHandle:指定对象或事件的句柄;
dwMilliseconds: 等待时间,以毫妙为单位,当超过等待时间时,此函数将返回。如果该参数设置为0,则该函数立即返回,如果设置为INFINITE,则该函数直到有信号才返回。
返回值:
如果此函数成功,该函数的返回之标识了引起该函数返回的事件。返回值如下:
WAIT_ABANDONED(0x00000080L)
指定的对象是一个互斥对象,该对象没有被拥有该对象的线程在线程结束前释放。互斥对象的所有权被同意授予调用该函数的线程。互斥对象被设置成为无信号状态。
WAIT_OBJECT_0 (0x00000000L)
指定的对象出有有信号状态。
WAIT_TIMEOUT (0x00000102L)
超过等待时间,指定的对象处于无信号状态
如果失败,返回 WAIT_FAILED;
备注:
此函数检查指定的对象或事件的状态,如果该对象处于无信号状态,则调用线程处于等待状态,此时该线程不消耗CPU时间,
该函数可以等待如下对象:
Change notification
Console input
Event
Job
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
需求:
Client Requires Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98, or Windows 95.
Server Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server.
Header Declared in Winbase.h; include Windows.h.
Library Link to Kernel32.lib.
DLL Requires Kernel32.dll.
程序举例:
1、创建对话框应用程序,项目名称为MyTestThread
2、添加按钮,命名为启动和停止,在对话框中增加编辑框,ID为IDC_TIME,
3、增加成员变量,HANDLE m_hThread[2],此为线程的句柄;
4、定义全局变量,用来控制线程的运行与否;
volatile BOOL m_ThreadRun[2];
5、增加全局事件对象,用来监控线程,控制线程是否运行。
CEvent event;
注意:4、5定义的对象,必须在.cpp文件中定义;
6、声明回调函数。回调函数必须是全局函数或静态函数。声明方式如下:
void ThreadFunc1(LPVOID pParam);
void ThreadFunc2(LPVOID pParam);
回调函数的实现如下:
void ThreadFunc1(LPVOID pParam)
{
CTime time;
CString strTime;
event.ResetEvent();
m_ThreadRun[0] = true;
m_ThreadRun[1] = true;
DWORD ThreadID = ::GetCurrentThreadId();
while(m_ThreadRun[0])
{
time = CTime::GetCurrentTime();
strTime = time.Format("%H:%M:%S");
CMyTestThreadDlg* pDlg = (CMyTestThreadDlg*)pParam;
pDlg->SetDlgItemText(IDC_TIME,strTime);
Sleep(1000);
}
}
void ThreadFunc2(LPVOID pParam)
{
CTime time;
CString strTime;
DWORD ThreadID = ::GetCurrentThreadId();
//event为有信号状态,则下边的函数执行后,该线程则开始运行,如果event为无信号状态,则下边的函数执行
//后,该线程处于等待状态,直到有信号才开始运行;
::WaitForSingleObject(event,INFINITE);
while(m_ThreadRun[1])
{
time = CTime::GetCurrentTime();
strTime = time.Format("%H:%M:%S");
CMyTestThreadDlg* pDlg = (CMyTestThreadDlg*)pParam;
pDlg->SetDlgItemText(IDC_TIME,"OK");
Sleep(1000);
::WaitForSingleObject(event,INFINITE);
}
}
7、定义保存线程ID的成员变量:DWORD m_ThreadID[2];
8、对启动和停止按钮增加消息响应函数,如下:
void CMyTestThreadDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
m_hThread[0] = ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc1,this,0,&m_ThreadID[0]);
m_hThread[1] = ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc2,this,0,&m_ThreadID[1]);
GetDlgItem(IDC_BUTTON1)->EnableWindow(false);
GetDlgItem(IDC_BUTTON2)->EnableWindow(true);
}
void CMyTestThreadDlg::OnBnClickedCancel()
{
m_ThreadRun[0] = false;
event.SetEvent();
GetDlgItem(IDC_BUTTON1)->EnableWindow(true);
GetDlgItem(IDC_BUTTON2)->EnableWindow(false);
}
WaitForMultipleObjects是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象(关于该函数的描述和例子见MSDN,)。但同时该函数在用法上却需要一定的技巧。
原型:DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
当WaitForMultipleObjects等到多个内核对象的时候,如果它的bWaitAll 参数设置为false。其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被出发,这个函数返回的只是其中序号最小的那个。
问题就在这里,我们如何可以获取所有被同时触发的内核对象。举个例子:我们需要在一个线程中处理从完成端口、数据库、和可等待定时器来的数据。一个典型的实现方法就是:用WaitForMultipleObjects等待所有的这些事件。如果完成端口,数据库发过来的数据量非常大,可等待定时器时间也只有几十毫秒。那么这些事件同时触发的几率可以说非常大,我们不希望丢弃任何一个被触发的事件。那么如何能高效地实现这一处理呢?
MSDN中有一句非常重要的描述,它可以说是WaitForMultipleObjects用法的精髓:The function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one. When bWaitAll is FALSE, and multiple objects are in the signaled state, the function chooses one of the objects to satisfy the wait; the states of the objects not selected are unaffected.
多个内核对象被触发时,WaitForMultipleObjects选择其中序号最小的返回。而WaitForMultipleObjects它只会改变使它返回的那个内核对象的状态。
这儿又会产生一个问题,如果序号最小的那个对象频繁被触发,那么序号比它大的内核对象将的不到被出理的机会。
为了解决这一问题,可以采用双WaitForMultipleObjects
检测机制来实现。见下面的例子:
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
DWORD dwRet = 0;
int nIndex = 0;
while(1)
{
dwRet = WaitForMultipleObjects(nCount,pHandles,false,INFINITE);
switch(dwRet)
{
case WAIT_TIMEOUT:
break;
case WAIT_FAILED:
return 1;
default:
{
nIndex = dwRet - WAIT_OBJECT_0;
ProcessHanlde(nIndex++);
//同时检测其他的事件
while(nIndex < nCount)
{
dwRet = WaitForMultipleObjects(nCount - nIndex,&pHandles[nIndex],false,0);
switch(dwRet)
{
case WAIT_TIMEOUT:
nIndex = nCount; //退出检测,因为没有被触发的对象了.
break;
case WAIT_FAILED:
return 1;
default:
{
nIndex = dwRet - WAIT_OBJECT_0;
ProcessHanlde(nIndex++);
}
break
}
}
}
break;
}
}
return 0;
}