1.等待函数
1)WaitForSingleObjectDWORD WaitForSingleObject(
HANDLE hHandle, // handle to object DWORD dwMilliseconds // time-out interval );
功能说明:
等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.
参数:
hHandle ->内核对象句柄,可以是进程也可以是线程. (窗体不是内核对象,进程和线程是内核对象)
dwMilliseconds ->等待时间,单位是毫秒 INFINITE(-1)一直等待 (毫秒数过后不管内核对象的状态,等待的线程继续往下走)
返回值:
WAIT_OBJECT_0(0) 等待对象变为已通知
WAIT_TIMEOUT(0x102) 超时
(返回是那种原因导致等待的线程继续往下执行)
特别说明(内核对象的状态):
1】内核对象中的每种对象都可以说是处于已通知或未通知的状态之中
2】这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的
3】当线程正在运行的时候,线程内核对象处于未通知状态
4】当线程终止运行的时候,它就变为已通知状态
5】在内核中就是个BOOL值,运行时FALSE 结束TRUE(也就是一个结构中的某个成员的值)
注意:
函数的第一个参数为内核对象;
函数根据传入的内核对象的类型不同处理方式也不同;
如果内核对象是进程或线程,在目标内核对象变成已通知状态后不会将其改为未通知状态;
如果是其它内核对象就不一定了;
例如:连续两次调用该函数;
::WaitForSingleObject(hThread1, INFINITE);
::WaitForSingleObject(hThread1, INFINITE);
如果内核对象会在调用该函数后改为未通知状态,程序将卡死;
原因:
主线程第一次调用WaitForSingleObject等待线程1执行完;
线程1执行完,状态变为已通知;
但WaitForSingleObject将线程1的状态重新变为未通知;
主线程第二次调用WaitForSingleObject等待线程1变为已通知;
此时线程1已经执行完,不可能再主动变为已通知状态了;
主线程一直等待下去;
实例:
DWORD WINAPI ThreadProc1(LPVOID lpParameter) { for(int i=0;i<5;i++) { printf("+++++++++ "); Sleep(1000); } return 0; } int main(int argc, char* argv[]) { //创建一个新的线程 HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); //主线程将等待线程1进入已通知状态后才能继续运行;线程1运行完后自动进入已通知状态;也就是说输出5次+++++++++后才弹框; DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE); MessageBox(0,0,0,0); return 0; }
2)WaitForMultipleObjects
DWORD WaitForMultipleObjects( DWORD nCount, // number of handles in array CONST HANDLE *lpHandles, // object-handle array BOOL bWaitAll, // wait option DWORD dwMilliseconds // time-out interval );
功能说明:
同时查看若干个内核对象的已通知状态
参数:
nCount ->要查看内核对象的数量
lpHandles ->内核对象数组
bWaitAll ->等到类型 TRUE 等到所有变为已通知 FALSE 只要有一个变为已通知
dwMilliseconds ->超时时间 ;INFINITE一直等待
返回值:
bWaitAll为TRUE时,返回WAIT_OBJECT_0(0) 代码所以内核对象都变成已通知
bWaitAll为FALSE时,返回最先变成已通知的内核对象在数组中的索引
WAIT_TIMEOUT(0x102) 超时
实例:
DWORD WINAPI ThreadProc1(LPVOID lpParameter) { for(int i=0;i<5;i++) { printf("+++++++++ "); Sleep(1000); } return 0; } DWORD WINAPI ThreadProc2(LPVOID lpParameter) { for(int i=0;i<3;i++) { printf("--------- "); Sleep(1000); } return 0; } int main(int argc, char* argv[]) { HANDLE hArray[2]; //创建一个新的线程 HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); //创建一个新的线程 HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); hArray[0] = hThread1; hArray[1] = hThread2; DWORD dwCode = ::WaitForMultipleObjects(2, hArray,FALSE,INFINITE); //等到其中一个线程执行完变成已通知状态后弹框; MessageBox(0,0,0,0); return 0; }
2.互斥体
互斥:
一个全局变量或资源,可能有多个线程同时访问时会出现线程安全问题;
可以用互斥机制来解决:一个线程正访问目标资源时其它线程都不能访问;
临界区就是实现互斥的一种方式;
互斥体也是用来实现互斥的一种方式;
1)创建互斥体
api函数:
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // SD BOOL bInitialOwner, // initial owner LPCTSTR lpName // object name );
lpMutexAttributes ->三环程序一般传NULL;如果这个参数不为空一般就是内核对象;
bInitialOwner ->
lpName ->互斥体名称;如果另一个进程想打开该互斥体,需要用到
2)打开互斥体
HANDLE OpenMutex( DWORD dwDesiredAccess, // access BOOL bInheritHandle, // inheritance option LPCTSTR lpName // object name );
dwDesiredAccess ->权限;MUTEX_ALL_ACCESS、SYNCHRONIZE
bInheritHandle ->返回的句柄是否可继承
lpName ->目标互斥体名
3)使用互斥体
用在进程中通信时;
和等待函数配合使用;当一个进程访问互斥体时,其它进程无法访问同一名称的互斥体;
进程一: HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ"); 进程二: HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ"); WaitForSingleObject(g_hMutex,INFINITE); //逻辑代码 ReleaseMutex(g_hMutex); 进程三: HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ"); WaitForSingleObject(g_hMutex,INFINITE); //逻辑代码 ReleaseMutex(g_hMutex);
4)互斥体与临界区的区别:
1】临界区只能用于单个进程间的线程控制.
2】互斥体可以设定等待超时,但临界区不能.
3】线程意外终结时,Mutex可以避免无限等待.
4】Mutex效率没有临界区高.
互斥体是内核对象;
内核对象是在程序的低2G内存中创建的;
进程可以共享内核对象,因此互斥体能跨进程通信;
3.实例
目标:创建一个抢红包程序
第一步:在第一个文本框中输入一个值,比如1000
第二步:点击抢红包,同时创建3个线程,每个线程循环进行抢红包的操作,每次抢50
第三步:使用Mutex进行线程控制,当第一个文本框中的值<50时,强红包线程结束.
特别说明:
1】四个文本框中的值总和应该为1000
2】强红包线程每次延时50毫秒. (如果不延时,其中一个线程可能瞬间抢完);
3】使用WaitForMultipleObjects监听所有线程,当线程全部结束后 ;
调用CloseHandle关闭句柄. (线程运行完后自动结束,互斥体需要自己来关);
等待函数不能直接在WinMain的按钮点击事件中调用,否则主函数将等待导致无法接受消息循环而卡死;
解决办法是在按钮点击事件中创建一个新的线程,让新线程来创建和控制三个红包线程;
用临界区实现互斥:
#include<windows.h> #include<stdio.h> #include "resource.h" HWND hongbao; HWND zg; HWND wst; HWND smy; CRITICAL_SECTION cs; DWORD num; //zg DWORD WINAPI ThreadProc1( LPVOID lpParameter ){ while(num > 0){ EnterCriticalSection(&cs); //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR zgBuffer[10]; memset(zgBuffer, 0, 10); GetWindowText(zg, zgBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD zgNum; sscanf(zgBuffer, "%d", &zgNum); if(num > 0){ memset(szBuffer, 0, 10); memset(zgBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(zgBuffer, "%d", ++zgNum); SetWindowText(hongbao, szBuffer); SetWindowText(zg, zgBuffer); } LeaveCriticalSection(&cs); Sleep(10); } return 0; } //wst DWORD WINAPI ThreadProc2( LPVOID lpParameter ){ while(num > 0){ EnterCriticalSection(&cs); //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR wstBuffer[10]; memset(wstBuffer, 0, 10); GetWindowText(wst, wstBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD wstNum; sscanf(wstBuffer, "%d", &wstNum); if(num > 0){ memset(szBuffer, 0, 10); memset(wstBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(wstBuffer, "%d", ++wstNum); SetWindowText(hongbao, szBuffer); SetWindowText(wst, wstBuffer); } LeaveCriticalSection(&cs); Sleep(10); } return 0; } //smy DWORD WINAPI ThreadProc3( LPVOID lpParameter ){ while(num > 0){ EnterCriticalSection(&cs); //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR smyBuffer[10]; memset(smyBuffer, 0, 10); GetWindowText(smy, smyBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD smyNum; sscanf(smyBuffer, "%d", &smyNum); if(num > 0){ memset(szBuffer, 0, 10); memset(smyBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(smyBuffer, "%d", ++smyNum); SetWindowText(hongbao, szBuffer); SetWindowText(smy, smyBuffer); } LeaveCriticalSection(&cs); Sleep(10); } return 0; } //线程函数 DWORD WINAPI ThreadProc( LPVOID lpParameter // 给线程传递参数 ){ InitializeCriticalSection(&cs); //创建线程 HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL); return 0; } //回调函数 BOOL CALLBACK DialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_INITDIALOG : hongbao = GetDlgItem(hwndDlg, IDC_HB); zg = GetDlgItem(hwndDlg, TXT_ZG); wst = GetDlgItem(hwndDlg, TXT_WST); smy = GetDlgItem(hwndDlg, TXT_SMY); SetWindowText(zg, TEXT("0")); SetWindowText(wst, TEXT("0")); SetWindowText(smy, TEXT("0")); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BTN: //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); //清空输入框 SetWindowText(zg, TEXT("0")); SetWindowText(wst, TEXT("0")); SetWindowText(smy, TEXT("0")); HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); //如果不在其他的地方引用它 关闭句柄 ::CloseHandle(hThread); return TRUE; return FALSE; } break ; case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; } return FALSE ; } //程序入口 int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevHinstance, LPSTR lpCmdLine, int nCmdShow ){ //创建对话框窗口 DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc); return 0; }
用互斥体实现互斥:
#include<windows.h> #include<stdio.h> #include "resource.h" HWND hongbao; HWND zg; HWND wst; HWND smy; HANDLE g_hMutex; DWORD num; //zg DWORD WINAPI ThreadProc1( LPVOID lpParameter ){ while(num > 0){ WaitForSingleObject(g_hMutex,INFINITE); //等待互斥体 //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR zgBuffer[10]; memset(zgBuffer, 0, 10); GetWindowText(zg, zgBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD zgNum; sscanf(zgBuffer, "%d", &zgNum); if(num > 0){ memset(szBuffer, 0, 10); memset(zgBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(zgBuffer, "%d", ++zgNum); SetWindowText(hongbao, szBuffer); SetWindowText(zg, zgBuffer); } ReleaseMutex(g_hMutex); //释放互斥体 Sleep(10); } return 0; } //wst DWORD WINAPI ThreadProc2( LPVOID lpParameter ){ while(num > 0){ WaitForSingleObject(g_hMutex,INFINITE); //等待互斥体 //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR wstBuffer[10]; memset(wstBuffer, 0, 10); GetWindowText(wst, wstBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD wstNum; sscanf(wstBuffer, "%d", &wstNum); if(num > 0){ memset(szBuffer, 0, 10); memset(wstBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(wstBuffer, "%d", ++wstNum); SetWindowText(hongbao, szBuffer); SetWindowText(wst, wstBuffer); } ReleaseMutex(g_hMutex); //释放互斥体 Sleep(10); } return 0; } //smy DWORD WINAPI ThreadProc3( LPVOID lpParameter ){ while(num > 0){ WaitForSingleObject(g_hMutex,INFINITE); //等待互斥体 //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); TCHAR smyBuffer[10]; memset(smyBuffer, 0, 10); GetWindowText(smy, smyBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); DWORD smyNum; sscanf(smyBuffer, "%d", &smyNum); if(num > 0){ memset(szBuffer, 0, 10); memset(smyBuffer, 0, 10); sprintf(szBuffer, "%d", --num); sprintf(smyBuffer, "%d", ++smyNum); SetWindowText(hongbao, szBuffer); SetWindowText(smy, smyBuffer); } ReleaseMutex(g_hMutex); //释放互斥体 Sleep(10); } return 0; } //线程函数 DWORD WINAPI ThreadProc( LPVOID lpParameter // 给线程传递参数 ){ g_hMutex = CreateMutex(NULL,FALSE, "XYZ"); //创建互斥体 HANDLE hArray[3]; //创建线程 HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL); //等待所有线程执行完后关闭互斥体 hArray[0] = hThread1; hArray[1] = hThread2; hArray[2] = hThread3; DWORD dwCode = ::WaitForMultipleObjects(3, hArray,FALSE,INFINITE); ::CloseHandle(g_hMutex); return 0; } //回调函数 BOOL CALLBACK DialogProc( HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_INITDIALOG : hongbao = GetDlgItem(hwndDlg, IDC_HB); zg = GetDlgItem(hwndDlg, TXT_ZG); wst = GetDlgItem(hwndDlg, TXT_WST); smy = GetDlgItem(hwndDlg, TXT_SMY); SetWindowText(zg, TEXT("0")); SetWindowText(wst, TEXT("0")); SetWindowText(smy, TEXT("0")); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BTN: //获取文本框内容 TCHAR szBuffer[10]; memset(szBuffer, 0, 10); GetWindowText(hongbao, szBuffer, 10); //字符串转数字 sscanf(szBuffer, "%d", &num); //清空输入框 SetWindowText(zg, TEXT("0")); SetWindowText(wst, TEXT("0")); SetWindowText(smy, TEXT("0")); HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); //如果不在其他的地方引用它 关闭句柄 ::CloseHandle(hThread); return TRUE; return FALSE; } break ; case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; } return FALSE ; } //程序入口 int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevHinstance, LPSTR lpCmdLine, int nCmdShow ){ //创建对话框窗口 DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc); return 0; }