您好,登錄后才能下訂單哦!
同步對象使用實例
Win32窗口的建立:
我們將要學習的使用,分別是:互斥量,臨界區,事件,信號量.所以我們需要一個窗口,呈現四種四種同步對象狀態.
需要學到的目的有4點:
1 掌握內核同步對象的觸發規則(是內核同步對象)
2 弄懂同步等待成功引起的副作用
3 了解各個同步對象的運行流程
4 明白內核同步對象和用戶同步對象的異同點
一般掌握上面4種核心知識,就能放心大膽的使用多線程了。
首先創建一個Win32項目,不要選空項目;
我們需要四個小窗口,先找到注冊主窗口的代碼。
ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; 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)); return RegisterClassEx(&wcex); }
不重要的部分(就是Win32窗口流程):先創建的是注冊一個主窗口的Windows的類結構,并且賦值給他一個窗口的Proc函數,然后調用InitInstance創建一個主窗口.
子窗口創建: WM_CREATE 就是創建的
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
直接貼出完整代碼:
其中WndProc1是子窗口的消息處理函數
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; static int clientCX = 0; static int clientCY = 0; static TCHAR *szChildClass[] = { _T("Child1"), _T("Child2"), _T("Child3"), _T("Child4") };//子窗口名字 static WNDPROC childWndProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 };//子窗口的消息處理函數 static HWND hwndChild[4]; //子窗口句柄 switch (message) { case WM_CREATE: { //對四個UI窗口類進行統一初始化 WNDCLASSEX wcex; 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 (int i = 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; case WM_SIZE: { clientCX = LOWORD(lParam);//客戶區的寬度 clientCY = HIWORD(lParam);//客戶區的高度 for (int i = 0; i < CHILD_WND_COUNT; ++i) { // 移動窗口的位置和其大小 MoveWindow( hwndChild[i], (i % 2)*clientCX / 2, (i > 1)*clientCY / 2, clientCX / 2, clientCY / 2, TRUE ); } } break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜單選擇: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意繪圖代碼... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } LRESULT CALLBACK WndProc1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static THRPARAMS thrParams; int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_CREATE: { //系統中基于對話框字體的高度 int cyChar = HIWORD(GetDialogBaseUnits()); //填充THRPARAMS結構體 thrParams.hwnd = hWnd; thrParams.cyChar = cyChar; //創建一個當前線程沒有擁有所有權的 互斥對象 } break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜單選擇: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意繪圖代碼... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
1 演示創建一個Mutex互斥量
火車售票系統。
創建兩個線程,演示如何進行資源的保護。兩個線程競爭某個資源。
case WM_CREATE: { //系統中基于對話框字體的高度 int cyChar = HIWORD(GetDialogBaseUnits()); //填充THRPARAMS結構體 thrParams.hwnd = hWnd; thrParams.cyChar = cyChar; //創建一個當前線程沒有擁有所有權的 互斥對象 //FALSE技術遞歸計數器為0 線程id為0 所以是觸發狀態 g_hMutex = CreateMutex(NULL, FALSE, NULL); //創建兩個線程來賣火車票 HANDLE handleTicket1 = CreateThread(NULL, 0, ThrTicketProc1, &thrParams, 0, NULL); HANDLE handleTicket2 = CreateThread(NULL, 0, ThrTicketProc2, &thrParams, 0, NULL); /* 原因為:創建線程后返回了線程句柄,新創建的線程內核對象的使用計數是2,一個是線程本身,一個是 創建線程的線程,創建新的線程CloseHandle后,新的線程內核對象使用計數為1,當這個新線程結束運行后 內核對象的使用技術還要減1,這時內核對象的使用計數是0,則系統會自動刪除新線程的內核對象,這是 正常的處理流程. 如果不調用CloseHandle()則新線程運行結束后,由于使用計數為1,所以不會刪除線程的內核對象,這樣 就會造成內存泄漏,當然在整個程序運行結束后,操作系統會回首這些內存,因此可以知道如果不調用 CloseHandle的話,該程序在運行階段,會造成內存泄漏。 */ //關閉線程句柄 CloseHandle(handleTicket1); CloseHandle(handleTicket2); } //釋放互斥量對象的句柄 在窗口關閉前 case WM_DESTROY: // 關閉 互斥量句柄內存 刪除 CloseHandle(g_hMutex); PostQuitMessage(0); break;
來看這個線程函數怎么用:
兩個線程都對火車票的票數,也就是全局變量,來進行操作。
我們要避免就是同時兩個線程拿到這個變量,同時進行讀寫操作。
導致資源,脫離控制,要做到一個線程拿到這個資源立即鎖定,只有他
完成,其他線程才能進行訪問。
只需要修改其中的線程名輸出就可以觀測了.
DWORD WINAPI ThrTicketProc1(LPVOID lp) { //將輸入的參數 轉換成結構體 PPARAMS param = static_cast<PPARAMS>(lp); TCHAR szBuf[20] = { 0 }; HDC hdc; //進入死循環 while (true) { //等待函數 無限等待,知道g_hMutex這個互斥量對象觸發。 //不需要判斷返回值因為參數用的INFINITE,肯定有一個線程拿到這個所有權 WaitForSingleObject(g_hMutex, INFINITE); //如果票數大于0 g_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; } } return 0; }
我門發現井然有序,如果我們不釋放Release會造成死鎖,這樣其他
等待的線程,或永遠在等待,不會被觸發。
如果我們使用Wait等待函數,那WaitForSingleObject注釋掉。
兩個線程同時訪問一個資源,進行讀寫,導致資源脫離控制。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。