本文共 3855 字,大约阅读时间需要 12 分钟。
在多线程编程中,确保不同线程能够协调工作是开发者需要解决的重要问题。为了避免数据竞态和死锁等问题,C++ 提供了几种常用的同步机制。本文将从事件、临界区、信号量到互斥对象等多种方式进行详细分析,帮助开发者更好地理解和选择合适的同步机制。
事件对象是多线程编程中常用的同步机制之一。通过定义事件标志和等待事件的方式,开发者可以实现线程之间的通信和协调。在 C++ 中,事件对象可以通过 CreateEvent 和 SetEvent 等函数来创建和操作。
#includeint iGolbalCount = 0;int iMax = 12;HANDLE hEvent;int main() { hEvent = CreateEvent(NULL, FALSE, FALSE, "iGolbalCount"); if (!hEvent) { printf("创建事件对象失败!\n"); return 0; } SetEvent(hEvent); Sleep(1000); // 等待事件被设置 // 创建线程并启动 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(40000); // 等待线程完成 CloseHandle(hEvent); printf("主线程结束!\n"); return 0;}DWORD WINAPI ThreadProc1(LPVOID lpParam) { while (TRUE) { WaitForSingleObject(hEvent, INFINITE); if (iGolbalCount < iMax) { printf("这里是线程1,iGolbalCount=%d\n", iGolbalCount++); SetEvent(hEvent); } else { SetEvent(hEvent); break; } Sleep(10); } return 0;}
临界区是一种轻量级的同步机制,通过占用共享资源的方式来确保线程只能在一个线程执行。临界区的使用需要显式地使用 EnterCriticalSection 和 LeaveCriticalSection 函数。
#includeCRITICAL_SECTION cs;int main() { InitializeCriticalSection(&cs); Sleep(50000); // 等待线程启动 // 创建线程并启动 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(50000); // 等待线程完成 DeleteCriticalSection(&cs); printf("主线程结束!\n"); return 0;}DWORD WINAPI ThreadProc1(LPVOID lpParam) { while (TRUE) { EnterCriticalSection(&cs); if (iGolbalCount < iMax) { printf("这里是线程1,iGolbalCount=%d\n", iGolbalCount++); } else { break; } Sleep(10); LeaveCriticalSection(&cs); } return 0;}
信号量是一种更灵活的同步机制,通过等待和释放信号的方式来控制线程的执行。信号量可以支持多个等待对象,适用于需要动态控制线程数量的场景。
#includeHANDLE hSemaphore;int cMax = 1;int main() { hSemaphore = CreateSemaphore(NULL, cMax, cMax, NULL); if (!hSemaphore) { printf("创建信号量对象失败\n"); return 0; } Sleep(5000); // 等待线程启动 // 创建线程并启动 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(5000); // 等待线程完成 printf("主线程结束!\n"); return 0;}DWORD WINAPI ThreadProc1(LPVOID lpParam) { while (TRUE) { DWORD dwWaitResult = WaitForSingleObject(hSemaphore, 0L); if (dwWaitResult == WAIT_OBJECT_0) { if (iGolbalCount < iMax) { printf("这里是线程1,iGolbalCount=%d\n", iGolbalCount++); ReleaseSemaphore(hSemaphore, 1, NULL); } else { ReleaseSemaphore(hSemaphore, 1, NULL); break; } } Sleep(10); } return 0;}
互斥对象是一种更高级的同步机制,通过占用内存区间的方式来确保线程只能在一个线程执行。它比临界区更安全,且支持递归使用。
#includeHANDLE hMutex;int main() { hMutex = CreateMutex(NULL, TRUE, "iGolbalCount"); if (!hMutex) { printf("创建互斥对象失败!\n"); return 0; } Sleep(1000); // 等待线程启动 // 等待互斥对象并释放 WaitForSingleObject(hMutex, INFINITE); ReleaseMutex(hMutex); ReleaseMutex(hMutex); // 释放互斥对象 Sleep(5000); // 等待线程完成 printf("主线程结束!\n"); return 0;}DWORD WINAPI ThreadProc1(LPVOID lpParam) { while (TRUE) { WaitForSingleObject(hMutex, INFINITE); if (iGolbalCount < iMax) { printf("这里是线程1,iGolbalCount=%d\n", iGolbalCount++); } else { break; } Sleep(10); ReleaseMutex(hMutex); } return 0;}
在多线程编程中,选择合适的同步机制对于确保线程安全至关重要。无论是事件对象、临界区、信号量还是互斥对象,每种机制都有其适用的场景。开发者需要根据具体需求选择最合适的方式,同时注意正确使用和释放资源,以避免潜在的竞态条件和内存泄漏问题。
转载地址:http://epvfk.baihongyu.com/