同步对象使用实例


Win32窗口的建立:

我们将要学习的使用,分别是:互斥量,临界区,事件,信号量.所以我们需要一个窗口,呈现四种四种同步对象状态.


需要学到的目的有4点:

1 掌握内核同步对象的触发规则(是内核同步对象)

2 弄懂同步等待成功引起的副作用

3 了解各个同步对象的运行流程

4 明白内核同步对象和用户同步对象的异同点

一般掌握上面4种核心知识,就能放心大胆的使用多线程了。




首先创建一个Win32项目,不要选空项目;

我们需要四个小窗口,先找到注册主窗口的代码。

ATOMMyRegisterClass(HINSTANCEhInstance){WNDCLASSEXwcex;wcex.cbSize=sizeof(WNDCLASSEX);wcex.style=CS_HREDRAW|CS_VREDRAW;wcex.lpfnWndProc=WndProc;wcex.cbClsExtra=0;wcex.cbWndExtra=0;wcex.hInstance=hInstance;wcex.hIcon=LoadIcon(hInstance,MAKEINTRESOURCE(IDI_WIN32PROJECT1));wcex.hCursor=LoadCursor(NULL,IDC_ARROW);wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName=MAKEINTRESOURCE(IDC_WIN32PROJECT1);wcex.lpszClassName=szWindowClass;wcex.hIconSm=LoadIcon(wcex.hInstance,MAKEINTRESOURCE(IDI_SMALL));returnRegisterClassEx(&wcex);}



不重要的部分(就是Win32窗口流程):先创建的是注册一个主窗口的Windows的类结构,并且赋值给他一个窗口的Proc函数,然后调用InitInstance创建一个主窗口.



子窗口创建: WM_CREATE 就是创建的

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

直接贴出完整代码:


其中WndProc1是子窗口的消息处理函数

LRESULTCALLBACKWndProc(HWNDhWnd,UINTmessage,WPARAMwParam,LPARAMlParam){intwmId,wmEvent;PAINTSTRUCTps;HDChdc;staticintclientCX=0;staticintclientCY=0;staticTCHAR*szChildClass[]={_T("Child1"),_T("Child2"),_T("Child3"),_T("Child4")};//子窗口名字staticWNDPROCchildWndProc[]={WndProc1,WndProc2,WndProc3,WndProc4};//子窗口的消息处理函数staticHWNDhwndChild[4];//子窗口句柄switch(message){caseWM_CREATE:{//对四个UI窗口类进行统一初始化WNDCLASSEXwcex;wcex.cbSize=sizeof(WNDCLASSEX);wcex.style=CS_HREDRAW|CS_VREDRAW;wcex.cbClsExtra=0;wcex.cbWndExtra=0;wcex.hInstance=hInst;wcex.hIcon=NULL;wcex.hCursor=LoadCursor(NULL,IDC_ARROW);wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName=NULL;wcex.hIconSm=NULL;for(inti=0;i<CHILD_WND_COUNT;++i){//对不同的部分进行分别初始化wcex.lpfnWndProc=childWndProc[i];wcex.lpszClassName=szChildClass[i];//注册窗口类RegisterClassEx(&wcex);//创建窗口并且记录窗口句柄hwndChild[i]=CreateWindow(szChildClass[i],_T(""),WS_CHILD|WS_BORDER|WS_VISIBLE,0,0,0,0,hWnd,(HMENU)i,hInst,NULL);}}break;caseWM_SIZE:{clientCX=LOWORD(lParam);//客户区的宽度clientCY=HIWORD(lParam);//客户区的高度for(inti=0;i<CHILD_WND_COUNT;++i){//移动窗口的位置和其大小MoveWindow(hwndChild[i],(i%2)*clientCX/2,(i>1)*clientCY/2,clientCX/2,clientCY/2,TRUE);}}break;caseWM_COMMAND:wmId=LOWORD(wParam);wmEvent=HIWORD(wParam);//分析菜单选择:switch(wmId){caseIDM_ABOUT:DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUTBOX),hWnd,About);break;caseIDM_EXIT:DestroyWindow(hWnd);break;default:returnDefWindowProc(hWnd,message,wParam,lParam);}break;caseWM_PAINT:hdc=BeginPaint(hWnd,&ps);//TODO:在此添加任意绘图代码...EndPaint(hWnd,&ps);break;caseWM_DESTROY:PostQuitMessage(0);break;default:returnDefWindowProc(hWnd,message,wParam,lParam);}return0;}LRESULTCALLBACKWndProc1(HWNDhWnd,UINTmessage,WPARAMwParam,LPARAMlParam){staticTHRPARAMSthrParams;intwmId,wmEvent;PAINTSTRUCTps;HDChdc;switch(message){caseWM_CREATE:{//系统中基于对话框字体的高度intcyChar=HIWORD(GetDialogBaseUnits());//填充THRPARAMS结构体thrParams.hwnd=hWnd;thrParams.cyChar=cyChar;//创建一个当前线程没有拥有所有权的互斥对象}break;caseWM_COMMAND:wmId=LOWORD(wParam);wmEvent=HIWORD(wParam);//分析菜单选择:switch(wmId){caseIDM_ABOUT:DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUTBOX),hWnd,About);break;caseIDM_EXIT:DestroyWindow(hWnd);break;default:returnDefWindowProc(hWnd,message,wParam,lParam);}break;caseWM_PAINT:hdc=BeginPaint(hWnd,&ps);//TODO:在此添加任意绘图代码...EndPaint(hWnd,&ps);break;caseWM_DESTROY:PostQuitMessage(0);break;default:returnDefWindowProc(hWnd,message,wParam,lParam);}return0;}




1 演示创建一个Mutex互斥量


火车售票系统。

创建两个线程,演示如何进行资源的保护。两个线程竞争某个资源。



caseWM_CREATE:{//系统中基于对话框字体的高度intcyChar=HIWORD(GetDialogBaseUnits());//填充THRPARAMS结构体thrParams.hwnd=hWnd;thrParams.cyChar=cyChar;//创建一个当前线程没有拥有所有权的互斥对象//FALSE技术递归计数器为0线程id为0所以是触发状态g_hMutex=CreateMutex(NULL,FALSE,NULL);//创建两个线程来卖火车票HANDLEhandleTicket1=CreateThread(NULL,0,ThrTicketProc1,&thrParams,0,NULL);HANDLEhandleTicket2=CreateThread(NULL,0,ThrTicketProc2,&thrParams,0,NULL);/*原因为:创建线程后返回了线程句柄,新创建的线程内核对象的使用计数是2,一个是线程本身,一个是创建线程的线程,创建新的线程CloseHandle后,新的线程内核对象使用计数为1,当这个新线程结束运行后内核对象的使用技术还要减1,这时内核对象的使用计数是0,则系统会自动删除新线程的内核对象,这是正常的处理流程.如果不调用CloseHandle()则新线程运行结束后,由于使用计数为1,所以不会删除线程的内核对象,这样就会造成内存泄漏,当然在整个程序运行结束后,操作系统会回首这些内存,因此可以知道如果不调用CloseHandle的话,该程序在运行阶段,会造成内存泄漏。*///关闭线程句柄CloseHandle(handleTicket1);CloseHandle(handleTicket2);}//释放互斥量对象的句柄在窗口关闭前caseWM_DESTROY://关闭互斥量句柄内存删除CloseHandle(g_hMutex);PostQuitMessage(0);break;




来看这个线程函数怎么用:

两个线程都对火车票的票数,也就是全局变量,来进行操作。

我们要避免就是同时两个线程拿到这个变量,同时进行读写操作。

导致资源,脱离控制,要做到一个线程拿到这个资源立即锁定,只有他

完成,其他线程才能进行访问。

只需要修改其中的线程名输出就可以观测了.

DWORDWINAPIThrTicketProc1(LPVOIDlp){//将输入的参数转换成结构体PPARAMSparam=static_cast<PPARAMS>(lp);TCHARszBuf[20]={0};HDChdc;//进入死循环while(true){//等待函数无限等待,知道g_hMutex这个互斥量对象触发。//不需要判断返回值因为参数用的INFINITE,肯定有一个线程拿到这个所有权WaitForSingleObject(g_hMutex,INFINITE);//如果票数大于0g_trainTickets是一个全局变量if(g_trainTickets>0){//在这里休眠一下,暂时放弃剩余的时间片Sleep(800);//销售火车票//打印表示哪个线程销售的火车票wsprintf(szBuf,_T("线程1剩余火车票:%d"),g_trainTickets--);//获得绘图句柄hdc=GetDC(param->hwnd);//将字体绘制到子窗口中TextOut(hdc,0,g_iLine*param->cyChar,szBuf,lstrlen(szBuf));ReleaseDC(param->hwnd,hdc);//清空字符串memset(szBuf,0,sizeof(TCHAR)*20);//全局变量行数g_iLine++;//整个子窗口重新绘制InvalidateRect(param->hwnd,NULL,FALSE);//解锁释放这个互斥量对象使他触发ReleaseMutex(g_hMutex);}else{//解锁释放这个互斥量对象使他触发ReleaseMutex(g_hMutex);break;}}return0;}


我门发现井然有序,如果我们不释放Release会造成死锁,这样其他

等待的线程,或永远在等待,不会被触发。


如果我们使用Wait等待函数,那WaitForSingleObject注释掉。

两个线程同时访问一个资源,进行读写,导致资源脱离控制。