• 操作系统 多线程访问共享资源产生的问题


    首先我们假设一种情况  创建了10个线程  这些线程都执行一个线程函数 (都做一个事)

    如  我们设置了是个窗口 每个窗口都负责买火车票  我们的火车票的总数为100

    卖完为止  

    我们定义了一个全局变量

    long ticket=100; 为票数

    每卖出一张票  票数--

    这里我们需要创建了10个线程  这些线程都执行一个线程函数 (买票)

    但是 在多个线程同时访问一个共享资源时  会产生并发问题 

    如:  多个线程同时卖同一张票

    那么为了解决并发问题  我么的

    解决方案:线程同步

    线程同步方式:

    1.原子访问:同一时间 只允许一个线程访问资源 volatile防止编译优化,对特殊地址的稳定访问.  直接对内存进行操作

    这里想对票数--  可以用

    InterlockedDecrement(&ticket);  他会对票数--  直接对内存进行操作

    同时避免进程并行问题

    CRITICAL_SECTION m_cs;

    2.临界区(关键段)(用户模式下的线程同步 ):同一时刻只允许一个进程访问代码段

    分为:

    (1)直接阻塞:如果已经有线程在执行代码段  他就会等待  直到那个线程执行完代码后  锁会随机分配给一个等待的进程

    主函数中:

    1. //初始化关键段
    2. //InitializeCriticalSection(&m_cs);

    线程函数中:

    1. EnterCriticalSection(&m_cs);
    2. if(ticket==0)
    3. {
    4. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    5. LeaveCriticalSection(&m_cs);
    6. break;
    7. }
    8. ticket--;
    9. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    10. 锁的出口
    11. LeaveCriticalSection(&m_cs);

    (2)旋转锁:如果已经有线程在执行代码段  他会等待一定的时间(由我们自己决定)  如果等待时间到了  代码段其他线程还是    没执行完  他就会走  不继续等待

    这里不好测试  不直观  就不进行演示

    (3)非阻塞:如果已经有线程在执行代码段 他就会走  不继续等待

    主函数中:

    1. //初始化关键段
    2. //InitializeCriticalSection(&m_cs);

    线程函数中:

    1. while(1)
    2. {
    3. if(ticket==0)
    4. {
    5. printf("Thread id[%d] go\n",GetCurrentThreadId());
    6. break;
    7. }
    8. Sleep(10);
    9. if(!TryEnterCriticalSection(&m_cs))
    10. continue;//走开
    11. //EnterCriticalSection(&m_cs);
    12. if(ticket==0)
    13. {
    14. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    15. LeaveCriticalSection(&m_cs);
    16. break;
    17. }
    18. ticket--;
    19. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    20. LeaveCriticalSection(&m_cs);
    21. }
    22. return 0;

    线程函数完整代码如下

    1. DWORD WINAPI ThreadProc(LPVOID lpParameter)
    2. {
    3. while(1)
    4. {
    5. if(ticket==0)
    6. {
    7. printf("Thread id[%d] go\n",GetCurrentThreadId());
    8. break;
    9. }
    10. Sleep(10);
    11. //原子访问
    12. //InterlockedDecrement(&ticket);//对共享资源执行--操作
    13. //锁的入口 只允许一个进程访问下列关键段
    14. //if(!TryEnterCriticalSection(&m_cs))//
    15. //continue;//走开
    16. //EnterCriticalSection(&m_cs);
    17. //事件的判断 看有没有信号 没信号就走
    18. if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
    19. {
    20. continue;
    21. }
    22. //WaitForSingleObject(m_hMutex,INFINITE);
    23. if(ticket==0)
    24. {
    25. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    26. //SetEvent(m_hEvent);
    27. //ReleaseMutex(m_hMutex);
    28. //LeaveCriticalSection(&m_cs);
    29. ReleaseSemaphore(m_hSemaphore,1,NULL);
    30. break;
    31. }
    32. ticket--;
    33. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    34. //锁的出口
    35. //LeaveCriticalSection(&m_cs);
    36. //信号量
    37. ReleaseSemaphore(m_hSemaphore,1,NULL);
    38. //事件
    39. //SetEvent(m_hEvent);
    40. //互斥量
    41. //ReleaseMutex(m_hMutex);
    42. }
    43. return 0;
    44. }

    3.内核对象:同一时刻只允许一个线程访问代码段

    (1)互斥量:一直等互斥量  直到等到为止  执行代码

    1. //初始化互斥量
    2. //m_hMutex=CreateMutex(0,//安全属性
    3. // 1,//初始拥有权
    4. // L"MyMutex"//名字
    5. // );
    6. //ReleaseMutex(m_hMutex);

    1. DWORD WINAPI ThreadProc(LPVOID lpParameter)
    2. {
    3. while(1)
    4. {
    5. if(ticket==0)
    6. {
    7. printf("Thread id[%d] go\n",GetCurrentThreadId());
    8. break;
    9. }
    10. Sleep(10);
    11. WaitForSingleObject(m_hMutex,INFINITE);
    12. if(ticket==0)
    13. {
    14. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    15. ReleaseMutex(m_hMutex);
    16. break;
    17. }
    18. ticket--;
    19. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    20. //互斥量
    21. ReleaseMutex(m_hMutex);
    22. }
    23. return 0;
    24. }

    (2)事件  分配事件信号 谁 抢到谁执行

    1. //初始化事件
    2. //m_hEvent=CreateEventA(0,0,0,0);
    3. //SetEvent(m_hEvent);//初始信号为0 把信号设置为1 让线程去抢

    1. while(1)
    2. {
    3. if(ticket==0)
    4. {
    5. printf("Thread id[%d] go\n",GetCurrentThreadId());
    6. break;
    7. }
    8. Sleep(10);
    9. //事件的判断 看有没有信号 没信号就走
    10. if(WAIT_TIMEOUT==WaitForSingleObject(m_hEvent,10))
    11. {
    12. continue;
    13. }
    14. if(ticket==0)
    15. {
    16. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    17. SetEvent(m_hEvent);
    18. break;
    19. }
    20. ticket--;
    21. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    22. //事件
    23. SetEvent(m_hEvent);
    24. }
    25. return 0;

    (3)信号量:允许同一时间内指定数量的线程访问代码段

    1. //初始化信号量
    2. m_hSemaphore=CreateSemaphoreW(0,//安全属性
    3. 1,//初始化信号量总数
    4. 10,//最大信号量总数
    5. 0//名字
    6. );

    1. DWORD WINAPI ThreadProc(LPVOID lpParameter)
    2. {
    3. while(1)
    4. {
    5. if(ticket==0)
    6. {
    7. printf("Thread id[%d] go\n",GetCurrentThreadId());
    8. break;
    9. }
    10. Sleep(10);
    11. //信号的判断 看有没有信号 没信号就走
    12. if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
    13. {
    14. continue;
    15. }
    16. if(ticket==0)
    17. {
    18. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    19. ReleaseSemaphore(m_hSemaphore,1,NULL);
    20. break;
    21. }
    22. ticket--;
    23. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    24. //信号量
    25. ReleaseSemaphore(m_hSemaphore,1,NULL);
    26. }
    27. return 0;
    28. }

    上述三种内核对象需要在最后句柄释放

    1. if(m_hMutex)
    2. {
    3. CloseHandle(m_hMutex);
    4. m_hMutex=0;
    5. }
    6. if(m_hEvent)
    7. {
    8. CloseHandle(m_hEvent);
    9. m_hEvent=0;
    10. }
    11. if(m_hSemaphore)
    12. {
    13. CloseHandle(m_hSemaphore);
    14. m_hSemaphore=0;
    15. }

    我们在这里实现了单例模式

    1. //单例
    2. HANDLE hdl=OpenMutexW(MUTEX_ALL_ACCESS,0,L"MyMutex");
    3. if(hdl)
    4. {
    5. qDebug()<<"app is exists";
    6. exit(0);
    7. }

    完整代码如下

    1. #include
    2. #include
    3. #include
    4. long ticket=100;
    5. CRITICAL_SECTION m_cs;// 临街
    6. HANDLE m_hMutex;//互斥量
    7. HANDLE m_hEvent;//事件
    8. HANDLE m_hSemaphore;//信号量
    9. DWORD WINAPI ThreadProc(LPVOID lpParameter)
    10. {
    11. while(1)
    12. {
    13. if(ticket==0)
    14. {
    15. printf("Thread id[%d] go\n",GetCurrentThreadId());
    16. break;
    17. }
    18. Sleep(10);
    19. //原子访问
    20. //InterlockedDecrement(&ticket);//对共享资源执行--操作
    21. //锁的入口 只允许一个进程访问下列关键段
    22. //if(!TryEnterCriticalSection(&m_cs))//
    23. //continue;//走开
    24. //EnterCriticalSection(&m_cs);
    25. //事件的判断 看有没有信号 没信号就走
    26. if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
    27. {
    28. continue;
    29. }
    30. //WaitForSingleObject(m_hMutex,INFINITE);
    31. if(ticket==0)
    32. {
    33. printf("Thread id[%d] go-------\n",GetCurrentThreadId());
    34. //SetEvent(m_hEvent);
    35. //ReleaseMutex(m_hMutex);
    36. //LeaveCriticalSection(&m_cs);
    37. ReleaseSemaphore(m_hSemaphore,1,NULL);
    38. break;
    39. }
    40. ticket--;
    41. printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
    42. //锁的出口
    43. //LeaveCriticalSection(&m_cs);
    44. //信号量
    45. ReleaseSemaphore(m_hSemaphore,1,NULL);
    46. //事件
    47. //SetEvent(m_hEvent);
    48. //互斥量
    49. //ReleaseMutex(m_hMutex);
    50. }
    51. return 0;
    52. }
    53. int main(int argc, char *argv[])
    54. {
    55. QCoreApplication a(argc, argv);
    56. //单例
    57. HANDLE hdl=OpenMutexW(MUTEX_ALL_ACCESS,0,L"MyMutex");
    58. if(hdl)
    59. {
    60. qDebug()<<"app is exists";
    61. exit(0);
    62. }
    63. //初始化信号量
    64. m_hSemaphore=CreateSemaphoreW(0,//安全属性
    65. 1,//初始化信号量总数
    66. 10,//最大信号量总数
    67. 0//名字
    68. );
    69. //初始化事件
    70. //m_hEvent=CreateEventA(0,0,0,0);
    71. //SetEvent(m_hEvent);//初始信号为0 把信号设置为1 让线程去抢
    72. //初始化关键段
    73. //InitializeCriticalSection(&m_cs);
    74. //初始化互斥量
    75. //m_hMutex=CreateMutex(0,//安全属性
    76. // 1,//初始拥有权
    77. // L"MyMutex"//名字
    78. // );
    79. //ReleaseMutex(m_hMutex);
    80. HANDLE han=NULL;
    81. for(int i=0;i<10;i++)
    82. {
    83. han=CreateThread(0,0,&ThreadProc,0,0,0);
    84. if(han)
    85. {
    86. CloseHandle(han);
    87. han=0;
    88. }
    89. }
    90. a.exec();
    91. //销毁关键段
    92. //DeleteCriticalSection(&m_cs);
    93. //释放内核对象 互斥量/事件
    94. if(m_hMutex)
    95. {
    96. CloseHandle(m_hMutex);
    97. m_hMutex=0;
    98. }
    99. if(m_hEvent)
    100. {
    101. CloseHandle(m_hEvent);
    102. m_hEvent=0;
    103. }
    104. if(m_hSemaphore)
    105. {
    106. CloseHandle(m_hSemaphore);
    107. m_hSemaphore=0;
    108. }
    109. return a.exec();
    110. }

  • 相关阅读:
    VScode配置Jupyter
    【信息学CSP-J近16年历年真题64题】真题练习与解析 第9题之数字游戏
    C++ 反汇编 - 关于函数调用约定
    动态规划算法
    sql优化常用的几种方法
    php对接飞书机器人
    点云处理开发测试题目 实现2
    网易数帆自主创新再获认可:轻舟微服务入选信创技术图谱
    【3】c++11新特性(稳定性和兼容性)—>类成员的快速初始化
    Solidity之变量数据存储和作用域
  • 原文地址:https://blog.csdn.net/van9527/article/details/126192300