多线程程序需要注意两件事:互斥和同步;
互斥是多个线程访问同一个资源时,要保证同一时刻只能有一个线程访问该资源;
互斥常用的实现方式有:临界区和互斥体;
临界区无法跨进程,互斥体可以;
互斥体是内核对象,内核对象需要对0环进行操作,效率不如临界区;
同步是为了线程按顺序执;例如两个线程,希望某一个线程在执行前一定要保证另一个线程已经做了相应的操作;
可以用事件来实现简单的同步;
如果复杂的同步有时用事件无法实现;
信号量也可以用来实现同步;
1.关于信号量
信号量也是一个内核对象;
1)创建
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。0时不发送信号
第三个参数表示最大并发数量。lInitialCount<=lMaximumCount
第四个参数表示信号量的名称,传入NULL表示匿名信号量。
2)打开
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入FALSE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。
3)递增信号量的当前资源计数
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数返回当前资源数量的原始值,设为NULL表示不需要传出。
注:没有一个函数可以用来查询信标的当前资源数量的值
4)关闭
CloseHandle()
2.使用信号量
目标:
一个窗口程序,点击按钮go后开始文本框1中的数字开始累加;
当1累加到1000后,2、3、4中的两个开始累加直到100;
当其中有一个到100后剩下的文本框开始累加直到100;

思路:
用信号量来实现线程的同步;
在按钮go的点击事件中创建一个线程a;
线程a中创建一个信号量,lInitialCount的值设为0,表示最开始不释放信号;
在线程a中创建3个子线程1、2、3分别实现文本框2、3、4的累加;
每个子线程都调用wait函数等待信号;
线程a中实现文本框1的累加,然后调用ReleaseSemaphore释放2个信号量;此时有2个等待信号量的子线程可继续运行;
当其中一个运行的子线程结束后,调用ReleaseSemaphore释放1个信号量;最后一个等待的子线程可继续运行;
代码:
#include <windows.h> #include <stdio.h> #include "resource.h" HANDLE hSemaphore; HANDLE hThread[3]; HWND hEditSet; HWND hEdit1; HWND hEdit2; HWND hEdit3; DWORD WINAPI ThreadProc1(LPVOID lpParameter) { TCHAR szBuffer[10]; DWORD dwTimmer=0; WaitForSingleObject(hSemaphore, INFINITE); while(dwTimmer<100) { Sleep(100); memset(szBuffer,0,10); GetWindowText(hEdit1,szBuffer,10); sscanf( szBuffer, "%d", &dwTimmer ); dwTimmer++; memset(szBuffer,0,10); sprintf(szBuffer,"%d",dwTimmer); SetWindowText(hEdit1,szBuffer); } ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } DWORD WINAPI ThreadProc2(LPVOID lpParameter) { TCHAR szBuffer[10]; DWORD dwTimmer=0; WaitForSingleObject(hSemaphore, INFINITE); while(dwTimmer<100) { Sleep(100); memset(szBuffer,0,10); GetWindowText(hEdit2,szBuffer,10); sscanf( szBuffer, "%d", &dwTimmer ); dwTimmer++; memset(szBuffer,0,10); sprintf(szBuffer,"%d",dwTimmer); SetWindowText(hEdit2,szBuffer); } ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } DWORD WINAPI ThreadProc3(LPVOID lpParameter) { TCHAR szBuffer[10]; DWORD dwTimmer=0; WaitForSingleObject(hSemaphore, INFINITE); while(dwTimmer<100) { Sleep(100); memset(szBuffer,0,10); GetWindowText(hEdit3,szBuffer,10); sscanf( szBuffer, "%d", &dwTimmer ); dwTimmer++; memset(szBuffer,0,10); sprintf(szBuffer,"%d",dwTimmer); SetWindowText(hEdit3,szBuffer); } ReleaseSemaphore(hSemaphore, 1, NULL); return 0; } DWORD WINAPI ThreadBegin(LPVOID lpParameter) { TCHAR szBuffer[10]; DWORD dwMoney=0; hSemaphore = CreateSemaphore(NULL,0,3,NULL); //初始信号量为0,最大信号量为3 hThread[0] = ::CreateThread(NULL, 0, ThreadProc1,NULL, 0, NULL); hThread[1] = ::CreateThread(NULL, 0, ThreadProc2,NULL, 0, NULL); hThread[2] = ::CreateThread(NULL, 0, ThreadProc3,NULL, 0, NULL); while(dwMoney<1000) { memset(szBuffer,0,10); GetWindowText(hEditSet,szBuffer,10); sscanf( szBuffer, "%d", &dwMoney ); dwMoney++; memset(szBuffer,0,10); sprintf(szBuffer,"%d",dwMoney); SetWindowText(hEditSet,szBuffer); } ReleaseSemaphore(hSemaphore, 2, NULL); //释放2个信号量;可以使等待hSemaphore的2个线程开始运行; ::WaitForMultipleObjects(3, hThread,TRUE,INFINITE); //当3个线程运行完后会自动进入已通知状态,此时关闭信号量 ::CloseHandle(hSemaphore); return 0; } BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) { BOOL bRet = FALSE; switch(uMsg) { case WM_CLOSE: { EndDialog(hDlg,0); break; } case WM_INITDIALOG: { hEditSet = GetDlgItem(hDlg,IDC_EDIT1); hEdit1 = GetDlgItem(hDlg,IDC_EDIT2); hEdit2 = GetDlgItem(hDlg,IDC_EDIT3); hEdit3 = GetDlgItem(hDlg,IDC_EDIT4); SetWindowText(hEditSet,"0"); SetWindowText(hEdit1,"0"); SetWindowText(hEdit2,"0"); SetWindowText(hEdit3,"0"); break; } case WM_COMMAND: switch (LOWORD (wParam)) { case IDC_BUTTON1: { CreateThread(NULL, 0, ThreadBegin,NULL, 0, NULL); return TRUE; } } break ; } return bRet; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAIN),NULL,MainDlgProc); return 0; }