9.7 线程同步对象速查表
对象 |
何时处于未触发状态 |
何时处于触发状态 |
成功等待的副作用 |
进程 |
进程仍在运行的时候 |
进程终止的时(ExitProcess、TerminateProcess) |
没有 |
线程 |
线程仍在运行的时候 |
线程终止的时候(ExitThread、TermimateThread) |
没有 |
作业 |
作业尚未超时的时候 |
作业超时的时候 |
没有 |
文件 |
有待处理的I/O请求的时候 |
I/O请求完成的时候 |
没有 |
控制台输入 |
没有输入的时候 |
有输入的时候 |
没有 |
文件变更通知 |
文件没有变更的时候 |
文件系统检测到变更的时候 |
重置通知 |
自动重置事件 |
ResetEvent、PulseEvent或等待成功的时候 |
SetEvent/PulseEvent被调用的时候 |
重置事件 |
手动重置事件 |
ResetEvent、PulseEvent |
SetEvent/PulseEvent被调用的时候 |
没有 |
自动重置可等待计时器 |
CancelWaitableTimer或等待成功的时候 |
时间到的时候(SetWaitableTimer) |
重置计时器 |
手动重置可等待计时器 |
CancelWaitableTimer |
时间到的时候(SetWaitableTimer) |
没有 |
信号量 |
等待成功的时候 |
计数大于0的时候(ReleaseSemaphore) |
计数减1 |
互斥量 |
等待成功的时候 |
不为线程占用的时候(ReleaseMutex) |
把所有权交给线程 |
关键段 (用户模式) |
等待成功的时候 (Try)EnterCriticalSection |
不为线程占用的时候 (LeaveCriticalSection) |
把所有权交给线程 |
SRWLock (用户模式) |
等待成功的时候 (AcuquireSRWLock(Exclusive)) |
不为线程占用的时候 (ReleaseSRWLock(Exclusive)) |
把所有权交给线程 |
条件变量 (用户模式) |
等待成功的时候 (SleepConditionVaiable*) |
被唤醒的时候 (Wake(All)ConditionVariable) |
没有 |
InterLocked* (用户模式) |
从来不会使线程变成不可调度状态,它只是修改一个值并立即返回 |
9.8 其他的线程同步函数
9.8.1 WaitForInputIdle(hProcess,dwMilliseconds)函数
(1)等待进程,直到创建第一个窗口的线程处于输入“空闲”状态时(我的理解是这个线程消息队列中没有键盘和鼠标的消息了,这理解可能不准确!)。当父进程创建子进程时,父进程可以一边继续执行,一边让子进程初始化。这是父进程能够知道子进程己经初始化完毕的唯一方法,就是等待子进程,直到它不再处理任何输入为止。可以调用CreateProcess后,调用WaitForInputIdle。
(2)当要在程序中用模拟用发送键盘消息的方式来打开一个对话框里,也可以用WaitForInputIdle来等待这个对话框。如模拟“Alt+F,O”来打开“打开文件对话框”,可依次发送WM_KEYDOWN(VK_MENU)、WM_KEYDOWN(VK_F)、WM_KEYUP(VK_MENU)、WM_KEYUP(VK_F)、WM_KEYDOWN(VK_O)、WM_KEYUP(VK_O)
,此时系统会创建这个对话框,但由于对话框上可能还要多个子控件,这创建需要一定的时间,所以可以在发送键盘消息后,调用WaitForInputIdle来等待对话框创建完毕。
【WaitForInputIdle程序】模拟发送“Alt+F,0”组合键来打开“打开文件对话框”
#include <windows.h> #include <process.h> #include <locale.h> #include <tchar.h> //模拟按“ALT+F,O”组合键打开“Open Dialog”对话框 int _tmain() { _tsetlocale(LC_ALL, _T("chs")); //根据标题获o取窗体的句柄 HWND hwnd = FindWindow(_T("NotePad"), NULL); if (hwnd){ //通过窗体句柄获取记事本进程ID DWORD dwProcessID; GetWindowThreadProcessId(hwnd, &dwProcessID); //将进程ID转为进程的句柄 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); SetForegroundWindow(hwnd); //这句一定要加上去 //第1种方法 //keybd_event(VK_LMENU, 0, 0, 0); // 按 Alt //keybd_event(‘F‘, 0, 0, 0); //keybd_event(‘F‘, 0, KEYEVENTF_KEYUP, 0); // F弹上 //keybd_event(VK_LMENU, 0, KEYEVENTF_KEYUP, 0); // Alt弹上 //keybd_event(‘O‘, 0, 0, 0); //"O"键按下 //keybd_event(‘O‘, 0, KEYEVENTF_KEYUP, 0); // "O"键弹上 //第2种方法 INPUT input[6]; memset(input, 0, sizeof(input)); for (int i = 0; i < 6; i++){ input[i].type = INPUT_KEYBOARD; } //按键顺序 input[0].ki.wVk = input[3].ki.wVk = VK_LMENU; input[1].ki.wVk = input[2].ki.wVk = ‘F‘; input[4].ki.wVk = input[5].ki.wVk = ‘O‘; //按键状态(按下或释放) input[2].ki.dwFlags = input[3].ki.dwFlags = input[5].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(6, input, sizeof(INPUT)); if (hProcess){ WaitForInputIdle(hProcess, INFINITE);//等待对话框创建完成 _tprintf(_T("打开对话框%s!\n"), (GetLastError() == 0) ? _T("成功") : _T("失败")); CloseHandle(hProcess); } } _tsystem(_T("PAUSE")); return 0; }
9.8.2 MsgWaitForMultipleObjects(Ex)函数
(1)当内核对象被触发或当窗口消息被派送到一个由调用线程创建的窗口时都会将调用线程变为可调度状态。即该函数不仅可以等待对象触发,也可以等待指定的消息时触发。
(2)WaitForMultipleObjects会阻塞调用线程,因此以下两种线程:①创建窗口的线程②执行与用户界面相关的任务的线程,不应使用该函数。因为当线程被挂起时,用户在用户界面上的操作将无法得到响应。这时可使用MsgWaitForMultipleObjectEx来替代。
【MsgWaitForMultipleObjects的一般用法】
BOOL bLoop = TRUE; MSG msg; while (bLoop) { DWORD dwRet = MsgWaitForMultipleObjects(1, &hEventOk, FALSE, 10, QS_ALLINPUT); switch (dwRet) { case WAIT_OBJECT_0: //等待对象己触发 ...... //进行相应处理 bLoop = FALSE; //跳出循环 break; case WAIT_OBJECT_0 + 1: //界面消处到达 //从消息队列中获取消息并分派到指定的窗口 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){ TranslateMessage(&msg); DispatchMessage(&msg); } break; case WAIT_TIMEOUT: //超时处理 break; } }