ToB企服应用市场:ToB评测及商务社交产业平台

标题: Windows—线程根本知识和线程同步 [打印本页]

作者: 知者何南    时间: 2024-9-2 16:26
标题: Windows—线程根本知识和线程同步
线程

线程的组成

线程的进入点

每个线程必须拥有一个进入点函数,线程从这个进入点开始运行。
  1. DWORD WINAPI ThreadFunc(PVOID pvParam) {
  2.     DWORD dwResult = 0;
  3.     cout << "hello thank you!" << endl;
  4.     return dwResult;
  5. }
复制代码
你的线程函数可以执行你想要它做的任何任务。终极,线程函数到达它的末端处并且返回。 这时,线程终止运行,该堆栈的内存被释放,同时,线程的内核对象的利用计数被递减。假如利用计数降为0,线程的内核对象就被撤消 。
利用线程注意事项

:::info
CreateThread函数

  1. HANDLE WINAPI CreateThread(
  2.     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,//线程内核对象的默认安全属性,传递NULL。
  3.     _In_ SIZE_T dwStackSize,//将多少地址空间用于线程自己的堆栈
  4.     _In_ LPTHREAD_START_ROUTINE lpStartAddress,//线程函数地址,函数名
  5.     _In_opt_ __drv_aliasesMem LPVOID lpParameter,//线程函数的参数
  6.     _In_ DWORD dwCreationFlags,//0创建线程后立即调度
  7.     _Out_opt_ LPDWORD lpThreadId //线程ID
  8.     );
复制代码
  1. DWORD WINAPI ThreadFunc(PVOID pvParam) {
  2.     DWORD dwResult = 0;
  3.     cout << "hello thank you!" << endl;
  4.     return dwResult;
  5. }
  6. int main(){    CreateThread(NULL,0,ThreadFunc,NULL,0,0);     cout << "Hello World!"<<endl;}
复制代码

终止线程的运行

:::info
线程的初始化


:::info
线程的调度

sleep函数

线程优先级


:::info
一般来说,大多数时间高优先级的线程不应该处于可调度状态。当线程要举行某种利用时,它能灵敏获得CPU时间。这时线程应该尽大概少地执行CPU指令,并返回睡眠状态,等待再次变成可调度状态。相反,低优先级的线程可以保持可调度状态, 执行大量的CPU指令来举行它的利用。假如按照这些原则来办,整个利用系统就能精确地对用户作出相应。
:::
进程优先级


线程同步

什么时间必要线程同步

什么是句柄hHandle

线程同步的方式

:::info
实例

不同步例子

输出居然x=1
  1. int x = 0;
  2. DWORD WINAPI FirstThread(PVOID pvParam) {
  3.         x++;
  4.         return 0;
  5. }
  6.        
  7. DWORD WINAPI SecondThread(PVOID pvParam) {
  8.         x++;
  9.         return 0;
  10. }
  11.        
  12. int main(){
  13.         //主线程,线程1,线程2谁先运行完不知道
  14.         CreateThread(NULL,0, FirstThread,NULL,0,0);
  15.         CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  16.             
  17.         cout << "x="<<x<<endl;
  18.         return 0;
  19. }
复制代码
原子利用

原子访问:指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源。
  1. long x = 0;
  2. DWORD WINAPI FirstThread(PVOID pvParam) {
  3.         InterlockedExchangeAdd(&x, 1);
  4.         return 0;
  5. }
  6.        
  7. DWORD WINAPI SecondThread(PVOID pvParam) {
  8.         InterlockedExchangeAdd(&x, 1);
  9.         return 0;
  10. }
  11.        
  12. int main(){
  13.        
  14.         CreateThread(NULL,0, FirstThread,NULL,0,0);
  15.         CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  16.         Sleep(1);//主线程等等子线程
  17.             
  18.         cout << "x="<<x<<endl;
  19.         return 0;
  20. }
复制代码
临界区

:::info
包管在某一时间只有一个线程能访问数据的轻巧办法。在恣意时间只允许一个线程对共享资源举行访问。假如有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继承抢占,以此到达用原子方式利用共享资源的目的。
:::
临界区和互斥量的区别

:::info
1、临界区只能用于对象在同一进程里线程间的互斥访问;互斥体可以用于对象进程间或线程间的互斥访问。
2、临界区是非内核对象,只在用户态举行锁利用,速度快;互斥体是内核对象,在焦点态举行锁利用,速度慢。
3、临界区和互斥体在Windows平台下都可用;Linux下只有互斥体可用。
4、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,得当控制数据访问。
5、互斥量:为协调共同对一个共享资源的单独访问而计划的。
:::
临界区的利用步调

:::info
1 . 申请一个临界区变量 CRITICAL_SECTION gSection;
2. 初始化临界区 InitializeCriticalSection(&gSection);
3. 利用临界区 EnterCriticalSection(&gSection); …省略代码…LeaveCriticalSection(&gSection);
4.释放临界区 DeleteCriticalSection(&gSection);
:::
形象化解释

:::info
  1. long x = 0;
  2. CRITICAL_SECTION cs;
  3. DWORD WINAPI FirstThread(PVOID pvParam) {
  4.         EnterCriticalSection(&cs);
  5.         x++;
  6.         LeaveCriticalSection(&cs);
  7.         return 0;
  8. }
  9. DWORD WINAPI SecondThread(PVOID pvParam) {
  10.         EnterCriticalSection(&cs);
  11.         x++;
  12.         LeaveCriticalSection(&cs);
  13.         return 0;
  14. }
  15. int main() {
  16.         //初始化临界区
  17.         InitializeCriticalSection(&cs);
  18.         //创建线程,绑定入口函数
  19.         HANDLE handle1=CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  20.         HANDLE handle2=CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  21.         //主线程等待子线程结束,就不用特意Sleep了
  22.         WaitForSingleObject(handle1, INFINITE);
  23.         WaitForSingleObject(handle2, INFINITE);
  24.         //关闭线程句柄
  25.         CloseHandle(handle1);
  26.         CloseHandle(handle2);
  27.     //销毁
  28.         DeleteCriticalSection(&cs);
  29.         cout << "x=" << x << endl;
  30.         return 0;
  31. }
复制代码
临界区的长处

:::info
不关闭线程句柄的影响

:::info
假如不关闭线程句柄,大概会导致系统资源泄漏,特殊是在创建大量线程的环境下。这是由于每个线程句柄占用系统资源,并且在不再必要该线程时,应该通过CloseHandle来释放这些资源。假如你不关闭线程句柄,资源会一直占用,直到你的进程结束。
:::
互斥锁

:::info
  1. #include<mutex>
  2. long x = 0;
  3. mutex mx;
  4. DWORD WINAPI FirstThread(PVOID pvParam) {
  5.         mx.lock();
  6.         x++;
  7.         mx.unlock();
  8.         return 0;
  9. }
  10. DWORD WINAPI SecondThread(PVOID pvParam) {
  11.         mx.lock();
  12.         x++;
  13.         mx.unlock();
  14.         return 0;
  15. }
  16. int main() {
  17.         //创建线程,绑定入口函数
  18.         HANDLE handle1=CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  19.         HANDLE handle2=CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  20.         //主线程等待子线程结束
  21.         WaitForSingleObject(handle1, INFINITE);
  22.         WaitForSingleObject(handle2, INFINITE);
  23.         //关闭线程句柄
  24.         CloseHandle(handle1);
  25.         CloseHandle(handle2);
  26.         cout << "x=" << x << endl;
  27.         return 0;
  28. }
复制代码
信号量

:::info
  1. #include <iostream>
  2. #include <windows.h>
  3. using namespace std;
  4. long x = 0;
  5. HANDLE semaphore;
  6. DWORD WINAPI FirstThread(PVOID pvParam) {
  7.     WaitForSingleObject(semaphore, INFINITE);  // 等待信号量,进入临界区
  8.     x++;  // 修改共享资源
  9.     ReleaseSemaphore(semaphore, 1, NULL);  // 释放信号量,离开临界区
  10.     return 0;
  11. }
  12. DWORD WINAPI SecondThread(PVOID pvParam) {
  13.     WaitForSingleObject(semaphore, INFINITE);  // 等待信号量,进入临界区
  14.     x++;  // 修改共享资源
  15.     ReleaseSemaphore(semaphore, 1, NULL);  // 释放信号量,离开临界区
  16.     return 0;
  17. }
  18. int main() {
  19.     semaphore = CreateSemaphore(NULL, 1, 1, NULL);  // 创建信号量,初始值为1,最大值为1
  20.     // 创建线程,绑定入口函数
  21.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  22.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  23.     // 主线程等待子线程结束
  24.     WaitForSingleObject(handle1, INFINITE);
  25.     WaitForSingleObject(handle2, INFINITE);
  26.     // 关闭线程句柄
  27.     CloseHandle(handle1);
  28.     CloseHandle(handle2);
  29.     // 关闭信号量句柄
  30.     CloseHandle(semaphore);
  31.     cout << "x = " << x << endl;
  32.     return 0;
  33. }
复制代码
:::info

CreateSemaphore

  1. HANDLE CreateSemaphore(
  2.   LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性 (可选)
  3.   LONG lInitialCount,                          // 初始计数
  4.   LONG lMaximumCount,                          // 最大计数
  5.   LPCWSTR lpName                               // 信号量名称 (可选)
  6. );
复制代码
ReleaseSemaphore

lReleaseCount: 释放的计数值,即增加信号量计数器的值。此值必须大于0,表现你盼望释放的资源数量。比方,假如设置为1,信号量计数增加1,释放一个资源 。
  1. BOOL ReleaseSemaphore(
  2.   HANDLE hSemaphore,        // 信号量句柄
  3.   LONG lReleaseCount,       // 释放的计数
  4.   LPLONG lpPreviousCount    // 上一个计数 (可选)
  5. );
复制代码
条件变量

:::info
  1. #include <iostream>
  2. #include <windows.h>
  3. #include <condition_variable>
  4. #include <mutex>
  5. using namespace std;
  6. long x = 0;
  7. mutex mtx;
  8. condition_variable cv;
  9. bool ready = true;  // 初始状态为true, 第一个线程可以进入临界区
  10. DWORD WINAPI FirstThread(PVOID pvParam) {
  11.     unique_lock<mutex> lock(mtx);
  12.     cv.wait(lock, [] { return ready; });  // 等待条件变量,条件满足时继续执行
  13.     ready = false;  // 进入临界区后将条件设为false,阻止其他线程进入
  14.     x++;  // 修改共享资源
  15.     ready = true;  // 修改完共享资源后恢复条件,允许其他线程进入
  16.     lock.unlock();  // 解锁互斥锁
  17.     cv.notify_one();  // 通知其他等待线程
  18.     return 0;
  19. }
  20. DWORD WINAPI SecondThread(PVOID pvParam) {
  21.     unique_lock<mutex> lock(mtx);
  22.     cv.wait(lock, [] { return ready; });  // 等待条件变量,条件满足时继续执行
  23.     ready = false;  // 进入临界区后将条件设为false,阻止其他线程进入
  24.     x++;  // 修改共享资源
  25.     ready = true;  // 修改完共享资源后恢复条件,允许其他线程进入
  26.     lock.unlock();  // 解锁互斥锁
  27.     cv.notify_one();  // 通知其他等待线程
  28.     return 0;
  29. }
  30. int main() {
  31.     // 创建线程,绑定入口函数
  32.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  33.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  34.     // 主线程等待子线程结束
  35.     WaitForSingleObject(handle1, INFINITE);
  36.     WaitForSingleObject(handle2, INFINITE);
  37.     // 关闭线程句柄
  38.     CloseHandle(handle1);
  39.     CloseHandle(handle2);
  40.     cout << "x = " << x << endl;
  41.     return 0;
  42. }
复制代码
事件

:::info
  1. #include <iostream>
  2. #include <windows.h>
  3. using namespace std;
  4. long x = 0;
  5. HANDLE eventHandle;
  6. DWORD WINAPI FirstThread(PVOID pvParam) {
  7.     WaitForSingleObject(eventHandle, INFINITE);  // 等待事件变为有信号状态
  8.     x++;  // 修改共享资源
  9.     SetEvent(eventHandle);  // 设置事件为有信号状态,允许其他线程继续执行
  10.     return 0;
  11. }
  12. DWORD WINAPI SecondThread(PVOID pvParam) {
  13.     WaitForSingleObject(eventHandle, INFINITE);  // 等待事件变为有信号状态
  14.     x++;  // 修改共享资源
  15.     SetEvent(eventHandle);  // 设置事件为有信号状态,允许其他线程继续执行
  16.     return 0;
  17. }
  18. int main() {
  19.     // 创建自动重置事件对象,初始状态为有信号状态
  20.     eventHandle = CreateEvent(NULL, FALSE, TRUE, NULL);
  21.     // 创建线程,绑定入口函数
  22.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  23.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  24.     // 主线程等待子线程结束
  25.     WaitForSingleObject(handle1, INFINITE);
  26.     WaitForSingleObject(handle2, INFINITE);
  27.     // 关闭线程句柄
  28.     CloseHandle(handle1);
  29.     CloseHandle(handle2);
  30.     // 关闭事件句柄
  31.     CloseHandle(eventHandle);
  32.     cout << "x = " << x << endl;
  33.     return 0;
  34. }
复制代码
:::info
事件在某个线程调用WaitForSingleObject并成功进入临界区后,自动重置为无信号状态,制止其他线程进入临界区,确保线程间的互斥访问
:::
CreateEvent

  1. /*
  2. * 功能:创建一个事件对象。(有人说创建或打开一个命名的或无名的事件对象,
  3. 当名字为参4时,会返回已打开的事件对象,
  4. 但是我下面的案例测试是无法根据参4(无论是否为NULL都不行)获取已打开的对象)。
  5. * 返回值:返回一个句柄HANDLE。
  6. * 参1:属性,一般传NULL即可。
  7. * 参2:是否设置手动改变事件状态。false自动,true手动。
  8. * 参3:状态的初始值,分为无状态和有状态,false代表无状态,true代表有状态。
  9. * 参4:事件的名字,可以为NULL。
  10. */
  11. CreateEventW(
  12.     _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
  13.     _In_ BOOL bManualReset,
  14.     _In_ BOOL bInitialState,
  15.     _In_opt_ LPCWSTR lpName
  16. );
复制代码
SetEvent

  1. /*
  2. * 功能:设置状态为有状态。
  3. * 返回值:1成功,0失败,该返回值实际意义不大。
  4. * 参1:一个内核对象的句柄,不过主要是Event。
  5. */
  6. SetEvent(
  7.     _In_ HANDLE hEvent
  8. );
复制代码
ResetEvent

  1. /*
  2. * 功能:设置状态为无状态。
  3. * 返回值:1成功,0失败,该返回值实际意义不大。
  4. * 参1:一个内核对象的句柄,不过主要是Event。
  5. */
  6. ResetEvent(
  7.     _In_ HANDLE hEvent
  8. );
复制代码
WaitForSingleObject

  1. /*
  2. * 功能:阻塞等待状态改变返回。
  3. * 返回值:返回DWORD的值,一般使用宏去判断,若立即返回,返回值为WAIT_OBJECT_0;
  4.           超时返回WAIT_TIMEOUT;失败返回WAIT_FAILED。
  5. * 参1:一个内核对象的句柄,可以是Event,Mutex,Semaphore(信号量),Process,Thread。
  6. * 参2:等待时长,单位ms。
  7. *
  8. * 注意:参2的取值:
  9. * 1)传0:表示不阻塞,立即返回,返回值为WAIT_OBJECT_0。
  10. * 2)传>0:阻塞时长,超时时返回WAIT_TIMEOUT。
  11. * 3)传INFINITE:表示一直阻塞,直到等待句柄的状态发生改变。
  12. */
  13. WaitForSingleObject(
  14.     _In_ HANDLE hHandle,
  15.     _In_ DWORD dwMilliseconds
  16. );
复制代码
案例

:::info
需求:主线程通过叫醒线程2退却出while循环,线程2等到event2后叫醒线程1,线程1等待event1,线程1是手动改变状态,调用完WaitForSingleObject是无法自动改变状态为无状态,以是最退却出时必须手动调用ResetEvent将状态改变为无状态。最后主线程由于两个线程都退出后,主线程就会退出循环,并且接纳句柄。注意主线程阻塞等待的是两个线程,而两个线程等待的是事件。
:::
  1. #include <iostream>
  2. #include <string>
  3. #include <windows.h>
  4. #include <tchar.h>
  5. using namespace std;
  6. HANDLE h_event1 = NULL;
  7. HANDLE h_event2 = NULL;
  8. DWORD  FunProc1(LPVOID lpParameter);
  9. DWORD  FunProc2(LPVOID lpParameter);
  10. //h_event1初始状态为无信号时,WaitForSingleObject(h_event1, 300)
  11. DWORD  FunProc1(LPVOID lpParameter)
  12. {
  13.         cout << "线程1开始运行。\n" << endl;
  14.         while (1)
  15.         {
  16.                 int ret = WaitForSingleObject(h_event1, 7000);
  17.                 if (WAIT_OBJECT_0 == ret) {
  18.                         cout << "线程1等到event1\n" << endl;
  19.                         break;
  20.                 }
  21.                 else if (WAIT_TIMEOUT == ret) {
  22.                         cout << "线程1等待event1超时\n" << endl;
  23.                 }
  24.                 else if (WAIT_FAILED == ret) {
  25.                         cout << "线程1调用WaitForSingleObject失败\n" << endl;
  26.                 }
  27.                 else {
  28.                         cout << "线程1调用WaitForSingleObject返回未知结果\n" << endl;
  29.                         break;
  30.                 }
  31.         }
  32.         cout << "线程1等到了event1,线程1结束。\n" << endl;
  33.         ResetEvent(h_event1);//因为创建事件时信号改变设置为手动改变,所以必须自动调用改变为无信号
  34.         return 0;
  35. }
  36. DWORD FunProc2(LPVOID lpParameter)
  37. {
  38.         cout << "线程2开始运行。\n" << endl;
  39.         while (1) {
  40.                 int ret = WaitForSingleObject(h_event2, 3000);//因为创建事件设置为自动,收到信号不阻塞后,该函数返回自动将状态改为无信号状态
  41.                 if (WAIT_OBJECT_0 == ret) {
  42.                         cout << "线程2等到event2\n" << endl;
  43.                         break;
  44.                 }
  45.                 else if (WAIT_TIMEOUT == ret) {
  46.                         cout << "线程2等待event2超时\n" << endl;
  47.                 }
  48.                 else if (WAIT_FAILED == ret) {
  49.                         cout << "线程2调用WaitForSingleObject失败\n" << endl;
  50.                 }
  51.                 else {
  52.                         cout << "线程2调用WaitForSingleObject返回未知结果\n" << endl;
  53.                         break;
  54.                 }
  55.         }
  56.         cout << "线程2等到了event2,线程2结束,并唤醒线程1。\n" << endl;
  57.         Sleep(350);
  58.         SetEvent(h_event1);
  59.         return 0;
  60. }
  61. int main(int argc, char** argv)
  62. {
  63.         h_event1 = CreateEvent(NULL, true, false, _T("event_one"));//参2代表设置手动改变状态,参3代表初始状态为无状态
  64.         h_event2 = CreateEvent(NULL, false, false, _T("event_two"));//参2代表设置自动改变状态
  65.         HANDLE hThread1;
  66.         hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
  67.         HANDLE hThread2;
  68.         hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);
  69.         Sleep(5000);
  70.         SetEvent(h_event2);
  71.         //线程1或者线程2都没退出继续等待,注意每个线程阻塞过程中收到信号改变立马不阻塞,并且结束的线程下次调用WaitForSingleObject直接返回
  72.         while (WaitForSingleObject(hThread1, 150) != WAIT_OBJECT_0 || WaitForSingleObject(hThread2, 150) != WAIT_OBJECT_0)
  73.         {
  74.                 cout << "线程还没有结束,主程序等了150ms。\n" << endl;
  75.         }
  76.         cout << "主线程等待两个子线程结束完毕" << endl;
  77.         CloseHandle(hThread1);
  78.         CloseHandle(hThread2);
  79.         CloseHandle(h_event1);
  80.         CloseHandle(h_event2);
  81.         system("pause");
  82.         return 0;
  83. }
  84. //改成INFINITE就是一直阻塞
复制代码
WaitForMultiObject

  1. /*
  2. * 功能:同样是阻塞等待状态改变返回。
  3. * 返回值:返回DWORD的值,一般使用宏去判断,若立即返回,返回值为WAIT_OBJECT_0;
  4.           超时返回WAIT_TIMEOUT;失败返回WAIT_FAILED。
  5. * 参1:句柄的数量,最大值为MAXIMUM_WAIT_OBJECTS(64),
  6.         可以是Event,Mutex,Semaphore(信号量),Process,Thread。
  7. * 参2:句柄数组的指针。
  8. * 参3:等待的类型,如果为TRUE 则等待所有信号量有效再往下执行,
  9.         FALSE 当有其中一个信号量有效时就向下执行。
  10. * 参4:等待时长,单位ms。
  11. * 注意:参4的取值:
  12. * 1)传0:表示不阻塞,立即返回,返回值为WAIT_OBJECT_0。
  13. * 2)传>0:阻塞时长,超时时返回WAIT_TIMEOUT。
  14. * 3)传INFINITE:表示一直阻塞,直到等待句柄的状态发生改变。
  15. */
  16. WaitForMultipleObjects(
  17.     _In_ DWORD nCount,
  18.     _In_reads_(nCount) CONST HANDLE* lpHandles,
  19.     _In_ BOOL bWaitAll,
  20.     _In_ DWORD dwMilliseconds
  21. );
复制代码
案例

  1. m_threadShow = std::thread(std::mem_fn(&MainWindow::ShowData), this);
  2. MainWindow::~MainWindow()
  3. {
  4.     SetEvent(m_KillEvent);
  5.     if(m_threadShow.joinable())
  6.         m_threadShow.join();
  7.     delete ui;
  8. }
  9. void MainWindow::ShowData()
  10. {
  11.     while(1)
  12.     {
  13.         HANDLE sigs[2] = {m_KillEvent, m_showEvent};
  14.               const DWORD ret = WaitForMultipleObjects(2, sigs, false, dwMilliseconds);
  15.                 switch (ret) {
  16.                 case WAIT_OBJECT_0: {
  17.                         //数组的第一个事件:m_KillEvent发生会来到这里
  18.                         break;
  19.                 }
  20.                 case WAIT_OBJECT_0 + 1: {
  21.                         //数组的第二个事件:m_showEvent发生会来到这里
  22.                         break;
  23.                 }
  24.                 case WAIT_TIMEOUT: {
  25.                         // 超时来到这里
  26.                 }
  27.                 default: {
  28.                         // 其它未知返回值的处理
  29.                 }
  30.     }
  31. }
复制代码
读写锁

:::info

  1. #include <iostream>
  2. #include <windows.h>
  3. using namespace std;
  4. long x = 0;
  5. SRWLOCK rwLock;
  6. DWORD WINAPI FirstThread(PVOID pvParam) {
  7.     AcquireSRWLockExclusive(&rwLock);  // 获取写锁,进入临界区
  8.     x++;  // 修改共享资源
  9.     ReleaseSRWLockExclusive(&rwLock);  // 释放写锁,离开临界区
  10.     return 0;
  11. }
  12. DWORD WINAPI SecondThread(PVOID pvParam) {
  13.     AcquireSRWLockExclusive(&rwLock);  // 获取写锁,进入临界区
  14.     x++;  // 修改共享资源
  15.     ReleaseSRWLockExclusive(&rwLock);  // 释放写锁,离开临界区
  16.     return 0;
  17. }
  18. int main() {
  19.     InitializeSRWLock(&rwLock);  // 初始化读写锁
  20.     // 创建线程,绑定入口函数
  21.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, 0);
  22.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, 0);
  23.     // 主线程等待子线程结束
  24.     WaitForSingleObject(handle1, INFINITE);
  25.     WaitForSingleObject(handle2, INFINITE);
  26.     // 关闭线程句柄
  27.     CloseHandle(handle1);
  28.     CloseHandle(handle2);
  29.     cout << "x = " << x << endl;
  30.     return 0;
  31. }
复制代码
自旋锁

:::info

  1. #include <iostream>
  2. #include <atomic>
  3. #include <windows.h>
  4. using namespace std;
  5. long x = 0;
  6. std::atomic_flag lock = ATOMIC_FLAG_INIT;  // 初始化自旋锁
  7. void AcquireSpinLock() {
  8.     // 自旋直到获取锁
  9.     while (lock.test_and_set(std::memory_order_acquire)) {
  10.         // 可以插入短暂的暂停来降低CPU占用率
  11.         Sleep(0);  // 暂停一段时间,让其他线程有机会运行
  12.     }
  13. }
  14. void ReleaseSpinLock() {
  15.     lock.clear(std::memory_order_release);  // 释放锁
  16. }
  17. DWORD WINAPI FirstThread(PVOID pvParam) {
  18.     AcquireSpinLock();  // 获取自旋锁
  19.     x++;  // 修改共享资源
  20.     ReleaseSpinLock();  // 释放自旋锁
  21.     return 0;
  22. }
  23. DWORD WINAPI SecondThread(PVOID pvParam) {
  24.     AcquireSpinLock();  // 获取自旋锁
  25.     x++;  // 修改共享资源
  26.     ReleaseSpinLock();  // 释放自旋锁
  27.     return 0;
  28. }
  29. int main() {
  30.     // 创建线程,绑定入口函数
  31.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, NULL);
  32.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, NULL);
  33.     // 主线程等待子线程结束
  34.     WaitForSingleObject(handle1, INFINITE);
  35.     WaitForSingleObject(handle2, INFINITE);
  36.     // 关闭线程句柄
  37.     CloseHandle(handle1);
  38.     CloseHandle(handle2);
  39.     cout << "x = " << x << endl;
  40.     return 0;
  41. }
复制代码
:::info
Sleep(0) 的作用:

自旋锁和互斥锁的区别

:::info
长处: 在锁竞争不激烈和临界区很短的环境下,开销小,性能高。
缺点: 假如锁竞争激烈或持有时间长,会导致高 CPU 占用和性能下降。
长处: 在锁竞争高和临界区较长的环境下表现良好,能够有效地处置惩罚线程阻塞和上下文切换。
缺点: 大概引入额外的上下文切换开销,在锁竞争低的环境下性能较差
:::
共享内存

  1. #include <iostream>
  2. #include <windows.h>
  3. using namespace std;
  4. const int BUFFER_SIZE = sizeof(long);  // 共享内存的大小
  5. long* sharedMemory;  // 指向共享内存区域的指针
  6. HANDLE hMapFile;     // 文件映射对象的句柄
  7. HANDLE hMutex;       // 互斥锁的句柄
  8. DWORD WINAPI FirstThread(PVOID pvParam) {
  9.     WaitForSingleObject(hMutex, INFINITE);  // 获取互斥锁
  10.     (*sharedMemory)++;  // 修改共享资源
  11.     ReleaseMutex(hMutex);  // 释放互斥锁
  12.     return 0;
  13. }
  14. DWORD WINAPI SecondThread(PVOID pvParam) {
  15.     WaitForSingleObject(hMutex, INFINITE);  // 获取互斥锁
  16.     (*sharedMemory)++;  // 修改共享资源
  17.     ReleaseMutex(hMutex);  // 释放互斥锁
  18.     return 0;
  19. }
  20. int main() {
  21.     // 创建文件映射对象
  22.     hMapFile = CreateFileMapping(
  23.         INVALID_HANDLE_VALUE,    // 使用系统分页文件
  24.         NULL,                    // 默认安全属性
  25.         PAGE_READWRITE,          // 可读写
  26.         0,                       // 最大对象大小(高 32 位)
  27.         BUFFER_SIZE,             // 最大对象大小(低 32 位)
  28.         TEXT("SharedMemoryName") // 名称
  29.     );
  30.     if (hMapFile == NULL) {
  31.         cerr << "Could not create file mapping object. Error code: " << GetLastError() << endl;
  32.         return 1;
  33.     }
  34.     // 映射视图到进程地址空间
  35.     sharedMemory = (long*) MapViewOfFile(
  36.         hMapFile,                 // 文件映射对象的句柄
  37.         FILE_MAP_ALL_ACCESS,      // 可读写权限
  38.         0,                        // 文件映射偏移(高 32 位)
  39.         0,                        // 文件映射偏移(低 32 位)
  40.         BUFFER_SIZE               // 映射的大小
  41.     );
  42.     if (sharedMemory == NULL) {
  43.         cerr << "Could not map view of file. Error code: " << GetLastError() << endl;
  44.         CloseHandle(hMapFile);
  45.         return 1;
  46.     }
  47.     // 创建互斥锁
  48.     hMutex = CreateMutex(
  49.         NULL,            // 默认安全属性
  50.         FALSE,           // 不拥有初始状态
  51.         TEXT("Global\\MyMutex") // 名称
  52.     );
  53.     if (hMutex == NULL) {
  54.         cerr << "Could not create mutex. Error code: " << GetLastError() << endl;
  55.         UnmapViewOfFile(sharedMemory);
  56.         CloseHandle(hMapFile);
  57.         return 1;
  58.     }
  59.     // 初始化共享内存区域
  60.     *sharedMemory = 0;
  61.     // 创建线程
  62.     HANDLE handle1 = CreateThread(NULL, 0, FirstThread, NULL, 0, NULL);
  63.     HANDLE handle2 = CreateThread(NULL, 0, SecondThread, NULL, 0, NULL);
  64.     if (handle1 == NULL || handle2 == NULL) {
  65.         cerr << "Could not create threads. Error code: " << GetLastError() << endl;
  66.         CloseHandle(hMutex);
  67.         UnmapViewOfFile(sharedMemory);
  68.         CloseHandle(hMapFile);
  69.         return 1;
  70.     }
  71.     // 主线程等待子线程结束
  72.     WaitForSingleObject(handle1, INFINITE);
  73.     WaitForSingleObject(handle2, INFINITE);
  74.     // 关闭线程句柄
  75.     CloseHandle(handle1);
  76.     CloseHandle(handle2);
  77.     // 输出结果
  78.     cout << "x = " << *sharedMemory << endl;
  79.     // 清理资源
  80.     CloseHandle(hMutex);
  81.     UnmapViewOfFile(sharedMemory);
  82.     CloseHandle(hMapFile);
  83.     return 0;
  84. }
复制代码
屏障

:::info

  1. #include <iostream>
  2. #include <windows.h>
  3. using namespace std;
  4. int x = 0;
  5. HANDLE hBarrierEvent; // 屏障事件句柄
  6. DWORD WINAPI FirstThread(PVOID pvParam) {
  7.     x++;
  8.     SetEvent(hBarrierEvent); // 线程1完成工作,通知主线程
  9.     return 0;
  10. }
  11.        
  12. DWORD WINAPI SecondThread(PVOID pvParam) {
  13.     x++;
  14.     SetEvent(hBarrierEvent); // 线程2完成工作,通知主线程
  15.     return 0;
  16. }
  17.        
  18. int main(){
  19.     // 创建一个事件用于线程同步
  20.     hBarrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  21.    
  22.     // 主线程,线程1,线程2谁先运行完不知道
  23.     HANDLE hThread1 = CreateThread(NULL, 0, FirstThread, NULL, 0, NULL);
  24.     HANDLE hThread2 = CreateThread(NULL, 0, SecondThread, NULL, 0, NULL);
  25.     // 等待两个线程完成
  26.     WaitForSingleObject(hThread1, INFINITE);
  27.     WaitForSingleObject(hThread2, INFINITE);
  28.    
  29.     // 等待屏障事件被设置
  30.     WaitForSingleObject(hBarrierEvent, INFINITE);
  31.     cout << "x=" << x << endl;
  32.     // 释放资源
  33.     CloseHandle(hThread1);
  34.     CloseHandle(hThread2);
  35.     CloseHandle(hBarrierEvent);
  36.     return 0;
  37. }
复制代码
内核对象同步



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4