轉載:
https://blog.csdn.net/yss28/article/details/53646627css
《Win32多線程程序設計》–Jim Beveridge & Robert Wienerweb
「等待某個什麼東西」是線程經常須要作的事。等待是線程的「必要之惡」。
若是你沒有等待線程結束就莽撞地結束程序,線程會被系統強制結束掉——在它完成它的工做以前。
因爲讓線程停工是操做系統的責任,很合理地咱們會認爲操做系統也有責任讓其餘線程知道某個線程停工了。windows
這個函數要求操做系統停止線程動做,直到渡過某個指定時間以後才恢復。數組
#include <stdio.h>
#include <Windows.h>
DWORD WINAPI Thread(void *arg) {
// doing something
return 0;
}
int main(void) {
HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
Sleep(?); // 不可能事先知道要等待Thread多久
CloseHandle(hThread);
return 0;
}
使用 GetExitCodeThread() 能夠決定一個線程是否還在執行。多線程
#include <stdio.h>
#include <Windows.h>
DWORD WINAPI Thread(void *arg) {
// doing something
return 0;
}
int main(void) {
DWORD exitCode = 0;
HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
while (1) {
GetExitCodeThread(hThread, &exitCode); // 嚴重浪費 CPU 時間
if (STILL_ACTIVE != exitCode)
break;
}
CloseHandle(hThread);
return 0;
}
可當作一個新版的 Sleep() ,它可以在某個線程結束時(而不是某段時間結束時)被調用。svg
可以使用的核心對象有兩種狀態:激發與未激發。WaitForSingleObject() 會在目標物變成激發狀態時返回。函數
對象 | 說明 |
---|---|
Thread(線程) | 當線程結束時,線程對象即被激發。當線程還在進行時,則對象處於未激發狀態。 |
Process(進程) | 當進程結束時,進程對象即被激發。當進程還在進行時,則對象處於未激發狀態。 |
Event | Event 對象的狀態直接受控於應用程序所使用的三個Win32函數:SetEvent()、PulseEvent()、ResetEvent()。CreateEvent()和OpenEvent()均可以傳回一個event object handle。Event對象的狀態也能夠被操做系統設定。 |
Mutex | 若是mutex沒有被任何線程擁有,它就是處於激發狀態。一旦一個等待mutex的函數返回了,mutex也就自動重置爲未激發狀態。 |
Semaphore | Semaphore有點像mutex,但它有個計數器,能夠約束其擁有者(線程)的個數。當計數器內容大於 0時,semaphore處於激發狀態,當計數器內容等於0時,semaphore處於未激發狀態。 |
#include <stdio.h>
#include <Windows.h>
DWORD WINAPI Thread(void *arg) {
// doing something
return 0;
}
int main(void) {
HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE); // 等待,直到線程被激發
CloseHandle(hThread);
return 0;
}
容許你在同一時間等待一個以上的對象。你必須將一個由 handles 組成的數組交給此函數,並指定要等待其中一個對象或是所有的對象。ui
(範例:保持線程池中始終有3個線程在運行)spa
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define POOL_SIZE 3 // 線程池大小
#define TASK_NUM 6
DWORD WINAPI ThreadFunc(LPVOID n) {
srand(GetTickCount());
Sleep(rand()%5000+500);
printf("%d over\n", n);
return ((DWORD)n);
}
int main(void){
HANDLE hThrds[POOL_SIZE];
int pIdx = 0, tIdx;
DWORD rc;
for (tIdx = 1; tIdx <= TASK_NUM; tIdx++) {
if (tIdx > POOL_SIZE) {
rc = WaitForMultipleObjects(POOL_SIZE, hThrds, FALSE, INFINITE); // 等待hThrds數組中任意一個變爲激發狀態,返回其索引
pIdx = rc - WAIT_OBJECT_0;
assert(pIdx >= 0 && pIdx < POOL_SIZE);
printf("%d terminated\n", pIdx);
CloseHandle(hThrds[pIdx]);
}
hThrds[pIdx++] = CreateThread(NULL, 0, ThreadFunc, (LPVOID)pIdx, 0, NULL);
printf("Thread #%d launched (pIdx %d)\n", tIdx, pIdx);
}
WaitForMultipleObjects(POOL_SIZE, hThrds, TRUE, INFINITE); // 等待hThrds數組中全部線程變爲激發狀態
for (pIdx = 0; pIdx < POOL_SIZE; pIdx++)
CloseHandle(hThrds[pIdx]);
return EXIT_SUCCESS;
}
結果:
操作系統
Windows 程序中的「主消息循環」看起來像這個樣子:
while (GetMessage(&msg, NULL, 0, 0,))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage() 有點像是特殊版本的 WaitForSingleObject(),它等待消息而
不是核心對象。一旦你調用 GetMessage() ,除非有一個消息真正進入你的消息
隊列( message queue )之中,不然它不會返回。
若是你在主線程中正使用 WaitForSingleO bject()或 WaitForMultipleObjects()等待某個對象被激發,你根本沒有辦法回到主消息循環中去。
爲了解決這個問題,主消息循環必須修改,使它得以同時等待消息或是核心對象被激發。你必須使用一個 MsgWaitForMultipleObjects() 函數。這個函數很是相似WaitForMultipleObjects(),但它會在「對象被激發」或「消息到達隊列」時被喚醒而返回。MsgWaitForMultipleObjects() 多接受一個參數,容許指定哪些消息是觀察對象。