Loading... # 事件 原子操作:只用于简单的同步机制,只能对4字节数据进行算数运算 临界区:对一段代码实现保护操作,只能在一个进程中不同线程使用。无法检测由于线程崩溃造成的临界区无法释放问题。对于拥有者可以多次进入临界区,退出也需要多次。否则容易造成死锁 `<span style="color:#FFFF00">`内核对象 如果线程崩溃内核会释放资源 互斥体:内核对象,在不同进程的线程中实现对一段代码保护,能检测由于线程崩溃造成的互斥体释放问题。又有现成概念。它只能被拥有者线程释放,故而多线程间的不同回调函数的同步使用可能会出现问题 信号量:内核对象,没有拥有者概念,可以控制多个线程同时访问代码,并且给线程设置一个上线(被锁次数 事件:一个内核对象,没有拥有者概念,自主性非常强,由程序员自己设置 互斥:多个线程操作(读写)同一个资源,不在意顺寻 同步:多个线程需要按照某种顺序执行 【等待完成 异步:执行一件事情的时候,可以做其他时间,通常依赖函数 _kbhit【不关系有没有返回或 ``` int g_n = 0; DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { g_n++; } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { g_n++; } return 0; } HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread1 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` ![image.png](http://www.irohane.top/usr/uploads/2021/02/3803707201.png) # 线程 在多线程中,极为容易产生错误,造成错误的根源很简单 1. 无非是两个或者多个线程同时访问了共同资源,(比如全局变量、句柄、堆空间、),造成资源在不同线程中修改时出现不一致,访问错误 2. 多个县城对于资源访问需要按照一定的先后顺序,但是未按照预期来导致程序出错 `<span style="color:#FF0000">`windows 在运行线程的时候会分配给每个线程一个时段,如果时段结束将运行下个程序,如果这个进程运行了一般,下个进程从当前地址开始运行,执行完毕将会执行了覆盖操作 ## 原子操作 原子操作 【函数】就是在执行的过程中即便暂停别的进程也无法使用此地址进行操作,只能等操作完毕 缺点:只能操作简单的运算 只能是整数 | InterlockedIncrement | 给一个整型变量自增1 | | -------------------------- | ---------------------------------------------- | | interlocakedExchangeAdd | 为一个整型变量以原子方式加上一个数 | | interlockedEXchange | 将一个32为数以原子方式赋值给另一个数 | | interlockedExchange64 | | | interlockedCompareExchange | 如果两个数相等,就将另一个数赋值,不相等无效果 | ``` long g_n = 0; DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { InterlockedIncrement(&g_n); //g_n++; } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { InterlockedIncrement(&g_n); //g_n++; } return 0; } HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread1 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` ## 临界区 缺点:非内核对象崩溃后别的线程卡住,不能多线程使用,,临界区只在一个进程内使用无法再多进程环境下同步。 优点:执行一段代码能在同一时间运行一个代码 `<span style="color:#FFFF00">`拥有者概念,结构体,进入临界区的程序,就拥有了这个临界区,拥有者可以无限制调用EnterCriticalSection而不会被阻塞 | | | | ------------------------------------------------------------------- | ---------------- | | void InitalizeCrititicalSection(LPCRITICAL_SECTIONpCriticalSection) | 初始化一个临界区 | | void DeleteCriticalSection(LPCRITICAL_SECTIONpCriticalSection) | 销毁临界区 | | void EnterCriticalSection() | 临界区开始 | | void LeaveCriticalSection() | 临界区结束 | ``` UINT32 g_n = 0; CRITICAL_SECTION g_cs = {};//临界区 DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { EnterCriticalSection(&g_cs); //InterlockedIncrement(&g_n); g_n++; LeaveCriticalSection(&g_cs); } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { EnterCriticalSection(&g_cs); //InterlockedIncrement(&g_n); g_n++; LeaveCriticalSection(&g_cs); } return 0; } InitializeCriticalSection(&g_cs);// 初始化临界区 HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread1 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` ## 互斥体 1. 激发态/非激发态 2. 拥有权,与临界区类似 | | | | ------------ | -------------------------------- | | CreateMutex | 创建互斥体 | | ReleaseMutex | 解锁互斥体 | | closeHandle | 关闭互斥体,将互斥体引用计数减一 | | OpenMutex | 打开一个互斥体 | 多个线程使用 某个线程崩溃自动释放拥有权,解锁激发态 ``` UINT32 g_n = 0; HANDLE g_hMutex = 0; //互斥体 DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { //WaitForSingleObject 实际上是引用计数-1 当计数减为0时应该是无信号状态。是针对对象 // 的计数检测 WaitForSingleObject(g_hMutex, -1); //等待其他线程释放 互斥体 g_n++; ReleaseMutex(g_hMutex); //手动解锁互斥体 } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { WaitForSingleObject(g_hMutex, -1); //等待其他线程释放 互斥体 g_n++; ReleaseMutex(g_hMutex); //手动解锁互斥体 } return 0; } //main 函数里 //CreateMutex 创建一开始就是有信号的 g_hMutex = CreateMutex ( NULL, FALSE, //创建线程,是不是第一拥有着 NULL ); //创建互斥体 HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread2 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` 利用互斥体 检测多开 ``` HANDLE g_hMutex = 0; //互斥体 //打开一个互斥体 g_hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, _T("irohane")); //如果能打开就代表已经运行一个客户端了 ,直接退出 if(NULL!=g_hMutex) { return 0; } else { //如果不能打开说明没有客户端运行,创建互斥体,正常运行程序 CreateMutex(0, 0, _T("irohane")); while (true) { printf("irohane"); } } ``` ## 信号量 互斥体可以堪称信号量的退化版 ,只有一个锁 | | | | ---------------- | ------------------------- | | CreateSemaphore | | | OpenSemaphore | | | ReleaseSemaphore | | | CloseHand | 关闭信号量,-1如果为0关闭 | | | | | | | ``` UINT32 g_n = 0; HANDLE g_hSemaphore = 0; DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { WaitForSingleObject(g_hSemaphore, -1); //等待其他线程释放 互斥体 g_n++; ReleaseSemaphore(g_hSemaphore,1,NULL); //手动解锁信号量 } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { WaitForSingleObject(g_hSemaphore, -1); //等待其他线程释放 互斥体 g_n++; ReleaseSemaphore(g_hSemaphore,1, NULL); //手动解锁信号量 } return 0; } //创建信号量 g_hSemaphore = CreateSemaphore(NULL, 1, 1, NULL); HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread1 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` ## 事件 手动设置对象激发态/非激发态 | | | | ------------ | ---------------- | | CreateEventW | 创建事件对象 | | OpenEventA | 打开事件对象 | | SetEvent | 设置激发态 | | ResetEvent | 设置事件为激发态 | | CLoseHandle | 关闭事件对象 | ``` UINT32 g_n = 0; HANDLE g_hEvent = 0; DWORD WINAPI ThreadPro1(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { WaitForSingleObject(g_hEvent, -1); //等待其他线程释放 互斥体 g_n++; SetEvent(g_hEvent); } return 0; } DWORD WINAPI ThreadPro2(LPVOID lpThreadParameter) { for (int i = 0;i < 100000;i++) { WaitForSingleObject(g_hEvent, -1); //等待其他线程释放 互斥体 g_n++; SetEvent(g_hEvent); } return 0; } g_hEvent = CreateEvent //创建事件 ( NULL, //安全属性 FALSE, //TRUE:手工重置 FALSE:自动充值 TRUE, //TRUE:初始化为激发态 FALSE初始化为非激发态 NULL ); HANDLE hThread1 = 0, hThread2 = 0; hThread1 = CreateThread(NULL, NULL, ThreadPro1, NULL, NULL, NULL); hThread1 = CreateThread(NULL, NULL, ThreadPro2, NULL, NULL, NULL); WaitForSingleObject(hThread1, -1); //第二个参数为等待时间 -1 是永久等待 WaitForSingleObject(hThread2, -1); printf("%d", g_n); ``` # 进程 ## 互斥体 ```cpp HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针 BOOL bInitialOwner, // 初始化互斥对象的所有者 LPCTSTR lpName // 指向互斥对象名的指针 ); #include "stdafx.h" #include "windows.h" int _tmain(int argc, _TCHAR* argv[]) { HANDLE g_Handle = CreateMutex(NULL, FALSE, "hello"); BOOL Reset = GetLastError(); if (g_Handle) { if (Reset == ERROR_ALREADY_EXISTS) { MessageBox(NULL, "程序已经运行", "错误提示", MB_OK); return 0; } } printf("hello world"); getchar(); } ``` 最后修改:2022 年 03 月 10 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏