最近在学习 Win32编程,所以顺便将每日所学记录下来,一方面为了巩固学习的知识,另一方面也为同样在学习Win32开发的童鞋们提供一份参考。
本系列博文均根据学习《WindowsAPI开发详解》一书总结而来;
运行环境:
- 操作系统: Windows 10家庭版
- 编译器:Visual Studio 2013
线程同步
同步原理
在多线程中线程同步很有意义,在线程同步过程中,需要先定义一个同步对象,同步对象一般具有两种状态:标志的和未标志的。
同步对象
最长用到的同步对象包括:Event、Mutex、Semaphore、Waitable;
等待函数
最常用于的就是WaitForSingleObject、WaitForMultipleObjects
WaitForSingleObject
1
| DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
|
1 2 3 4
| DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds );
|
同步对象
事件Event
CreateEvent
事件在线程同步中是最长使用的一种同步对象。
1 2 3 4
| HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPTSTR lpName);
|
SetEvent
将事件对象设置为标记;
1
| BOOL SetEvent(HANDLE hEvent );
|
ResetEvent
重置标记,如果事件是手工重置,那么需要该函数来重置事件
1
| BOOL ResetEvent( HANDLE hEvent );
|
OpenEvent
从事件名中得到事件句柄
1 2 3
| HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <Windows.h> #include <tchar.h> #include <stdio.h>
#define NUMTHREADS 3 #define BUFFER_SIZE 16 #define FOR_TIMES 5
HANDLE hEvent; BYTE lpSharedBuffer[16] = { 0 };
void UseEvent(); DWORD WINAPI ThreadProc(LPVOID lpParam);
VOID UseEvent() { HANDLE hThread; hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEvent == NULL) { printf("CreateEvent Error = %d\n", GetLastError()); return; } hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); if (hThread == NULL) { printf("CreateThread Error = %d\n", GetLastError()); return; } Sleep(2000); CopyMemory(lpSharedBuffer, "EVENT", strlen("EVENT") + 1); SetEvent(hEvent); }
DWORD WINAPI ThreadProc(LPVOID lpParam) { DWORD dwWaitResult; dwWaitResult = WaitForSingleObject(hEvent, INFINITE);
if (dwWaitResult != WAIT_OBJECT_0) { printf("Wait Error : %d\n", GetLastError()); return 0; } printf((CONST char *)lpSharedBuffer); if (!ResetEvent(hEvent)) { printf("SetEvent Error:%d\n", GetLastError()); return 1; } return 0; }
int main() { UseEvent(); getchar(); return 0; }
|
两个线程中使用事件对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
| #include <windows.h> #include <stdio.h>
#define NUMTHREADS 3 #define BUFFER_SIZE 16 #define FOR_TIMES 5
HANDLE hWriteEvent[NUMTHREADS]; HANDLE hReadEvents[NUMTHREADS]; BYTE lpSharedBuffer[16] = { 0 };
void MultiEvents(void); VOID WriteToBuffer(VOID); DWORD WINAPI ThreadFunction(LPVOID lpParam);
int main() { MultiEvents(); getchar(); return 0; }
void MultiEvents(void) { HANDLE hThread; DWORD i; for (i = 0; i < NUMTHREADS; i++) { hWriteEvent[i] = CreateEvent( NULL, FALSE, FALSE, NULL ); if (hWriteEvent[i] == NULL) { printf("CreateEvent failed (%d)\n", GetLastError()); return; } hReadEvents[i] = CreateEvent( NULL, FALSE, FALSE, NULL); if (hReadEvents[i] == NULL) { printf("CreateEvent failed (%d)\n", GetLastError()); return; } hThread = CreateThread(NULL, 0, ThreadFunction, (LPVOID)i, 0, NULL); if (hThread == NULL) { printf("CreateThread failed (%d)\n", GetLastError()); return; } } WriteToBuffer(); }
VOID WriteToBuffer(VOID) { DWORD dwWaitResult, j, i; for (j = 0; j < FOR_TIMES; j++) { Sleep(rand() % 100); wsprintf((LPWSTR)lpSharedBuffer, TEXT("shared %d"), j); for (i = 0; i<NUMTHREADS; i++) { if (!SetEvent(hWriteEvent[i])) { printf("SetEvent failed (%d)\n", GetLastError()); return; } } dwWaitResult = WaitForMultipleObjects( NUMTHREADS, hReadEvents, TRUE, INFINITE); if (dwWaitResult != WAIT_OBJECT_0) { printf("Wait error: %d\n", GetLastError()); ExitProcess(0); } } }
DWORD WINAPI ThreadFunction(LPVOID lpParam) { DWORD dwWaitResult; BYTE lpRead[16]; DWORD j = 0; DWORD dwThreadIndex = (DWORD)lpParam; for (; j<FOR_TIMES; j++) { dwWaitResult = WaitForSingleObject( hWriteEvent[dwThreadIndex], INFINITE); switch (dwWaitResult) { case WAIT_OBJECT_0: Sleep(rand() % 10); CopyMemory(lpRead, lpSharedBuffer, 16); break; default: printf("Wait error: %d\n", GetLastError()); ExitThread(0); } if (!SetEvent(hReadEvents[dwThreadIndex])) { printf("SetEvent failed (%d)\n", GetLastError()); return 0; } printf("线程 %u\t第 %d 次读,内容:%ls \r\n", dwThreadIndex, j, (LPSTR)lpRead); printf("\n"); } return 1; }
|
互斥对象Mutex
互斥对象具有以下特性:如果互斥对象没有被任何线程拥有,那么它是标记的,如果被一个线程拥有,那么它就是未标记的。
CreateMutex
创建互斥对象
1 2 3
| HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName );
|
OpenMutex
通过互斥对象名获取对象句柄;
dwDesiredAccess:对互斥量对象访问权限的设置,MUTEX_ALL_ACCESS 请求对互斥体的完全访问,MUTEX_MODIFY_STATE 允许使用 ReleaseMutex 函数,SYNCHRONIZE 允许互斥体对象同步使用;
1 2 3
| HANDLE OpenMutex( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName
|
ReleaseMutex
释放互斥对象,一个线程释放了互斥对象后,如果其他进程在等待,则可以获得该互斥对象;
1
| BOOL ReleaseMutex( HANDLE hMutex );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
#include <windows.h> #include <stdio.h>
#define NUM_THREADS 4
DWORD dwCounter = 0; HANDLE hMutex;
void UseMutex(void); DWORD WINAPI MutexThread(LPVOID lpParam);
int main() { UseMutex(); getchar(); }
void UseMutex(void) { INT i; HANDLE hThread;
hMutex = CreateMutex( NULL, FALSE, NULL); if (hMutex == NULL) { printf("CreateMutex error: %d\n", GetLastError()); }
for (i = 0; i < NUM_THREADS; i++) { hThread = CreateThread(NULL, 0, MutexThread, NULL, 0, NULL); if (hThread == NULL) { printf("CreateThread failed (%d)\n", GetLastError()); return; } } Sleep(1000); }
DWORD WINAPI MutexThread(LPVOID lpParam) {
DWORD dwWaitResult; dwWaitResult = WaitForSingleObject( hMutex, INFINITE);
switch (dwWaitResult) { case WAIT_OBJECT_0:
Sleep(rand() % 100); printf("counter: %d\n", dwCounter); dwCounter++;
if (!ReleaseMutex(hMutex)) { printf("Release Mutex error: %d\n", GetLastError()); } break; default: printf("Wait error: %d\n", GetLastError()); ExitThread(0); }
return 1; }
|
通常我们使用Mutex来禁止程序多开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <Windows.h> #include <tchar.h> #include <stdio.h>
int main() { HANDLE g_hMutex = CreateMutex(NULL, TRUE, TEXT("sYstemk1t")); DWORD dwMutexResults = GetLastError(); if (g_hMutex) { if (ERROR_ALREADY_EXISTS == dwMutexResults) { printf("程序已经打开了\n"); Sleep(2000); return 0; } } else { printf("创建失败\n"); return 0; } while (1) { Sleep(2000); printf("程序正在运行\n"); } return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <windows.h> #include <stdio.h> int main() { HANDLE g_hMutex = CreateMutex(0, FALSE, TEXT("sYstemk1t"));
WaitForSingleObject(g_hMutex, INFINITE);
for (int i = 0; i < 10; i++) { Sleep(1000); printf("B进程的Y线程:%d \n", i); } ReleaseMutex(g_hMutex); return 0; }
|
信号量
信号量维护了一个计数器,计数器的值可以在0到指定的最大值之间。当一个线程完成了对信号量的等待后,信号量计数器值减少。当一个线程释放信号量时,信号量计数器增加。
CreateSemaphore
创建信号量对象
1 2 3 4 5
| HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName );
|
OpenSemaphore
通过信号量命名,获得信号量对象句柄
1 2 3
| HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName
|
SEMAPHORE_ALL_ACCESS (0x1F0003) 要求对事件对象的完全访问;
SEMAPHORE_MODIFY_STATE (0x0002) 允许使用ReleaseSemaphore函数;
SYNCHRONIZE (0x00100000L)允许同步使用信号机对象。
ReleaseSemaphore
1 2 3
| BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
| #include <windows.h> #include <stdio.h>
#define NUMTHREADS 4
HANDLE hSemaphore;
void UseSemaphore(void); DWORD WINAPI SemaphoreThread(LPVOID lpParam);
int main() { UseSemaphore() ; getchar(); return 0; }
DWORD WaitForAllThread(HANDLE hThread[], DWORD dwNumThread) { DWORD dwWaitResult = WaitForMultipleObjects( dwNumThread, hThread, TRUE, INFINITE); switch (dwWaitResult) { case WAIT_OBJECT_0: printf("\nAll thread exit\n"); break; default: printf("\nWait error: %u", GetLastError()); } return 0;
}
void UseSemaphore(void) { HANDLE hThread[NUMTHREADS]; INT i; LONG lMax; CHAR cMax; printf("将创建%d个进程,获得信号量的进程可以向屏幕打印。\n" "请输入信号量的最大计数1~%d:",NUMTHREADS,NUMTHREADS); cMax = getch(); printf("%c\n",cMax); lMax = cMax & 0xF; if(lMax<0 || lMax>NUMTHREADS) { printf("请输入1-%d",NUMTHREADS); } hSemaphore = CreateSemaphore( NULL, lMax, lMax, NULL); if (hSemaphore == NULL) { printf("CreateSemaphore error: %d\n", GetLastError()); } for(i = 0; i < NUMTHREADS; i++) { hThread[i] = CreateThread(NULL, 0, SemaphoreThread, &i, 0, NULL); if (hThread[i] == NULL) { printf("CreateThread failed (%d)\n", GetLastError()); return; } } WaitForAllThread(hThread,NUMTHREADS); }
DWORD WINAPI SemaphoreThread(LPVOID lpParam) { DWORD dwWaitResult; BYTE lpRead[16]; DWORD dwPreviousCount;
DWORD j = 0; for(; j<3; j++) { Sleep(rand()%1000); dwWaitResult = WaitForSingleObject( hSemaphore, INFINITE); switch (dwWaitResult) { case WAIT_OBJECT_0: printf("\nProcess %d Gets Semaphore",GetCurrentThreadId()); break; default: printf("\nprocess %u Wait error: %u",GetCurrentThreadId(), GetLastError()); } Sleep(rand()%1000); if (!ReleaseSemaphore( hSemaphore, 1, &dwPreviousCount) ) { printf("\nprocess %u ReleaseSemaphore error: %d", GetCurrentThreadId(), GetLastError()); } else { printf("\nProcess %u release Semaphore, previous count is %u", GetCurrentThreadId(), dwPreviousCount); } }
return 1; }
|