转--秒杀多线程第四篇 一个经典的多线程同步问题

上一篇《秒杀多线程第三篇原子操作 Interlocked系列函数》中介绍了原子操作在多进程中的作用,现在来个复杂点的。这个问题涉及到线程的同步和互斥,是一道非常有代表性的多线程同步问题,如果能将这个问题搞清楚,那么对多线程同步也就打下了良好的基础。

程序描述:

主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。

要求:

1.子线程输出的线程序号不能重复。

2.全局变量的输出必须递增。

下面画了个简单的示意图:

分析下这个问题的考察点,主要考察点有二个:

1.主线程创建子线程并传入一个指向变量地址的指针作参数,由于线程启动须要花费一定的时间,所以在子线程根据这个指针访问并保存数据前,主线程应等待子线程保存完毕后才能改动该参数并启动下一个线程。这涉及到主线程与子线程之间的同步

2.子线程之间会互斥的改动和输出全局变量。要求全局变量的输出必须递增。这涉及到各子线程间的互斥

下面列出这个程序的基本框架,可以在此代码基础上进行修改和验证。

[cpp] view plaincopy

  1. //经典线程同步互斥问题
  2. #include <stdio.h>
  3. #include <process.h>
  4. #include <windows.h>
  5. long g_nNum; //全局资源
  6. unsigned int __stdcall Fun(void *pPM); //线程函数
  7. const int THREAD_NUM = 10; //子线程个数
  8. int main()
  9. {
  10. g_nNum = 0;
  11. HANDLE  handle[THREAD_NUM];
  12. int i = 0;
  13. while (i < THREAD_NUM)
  14. {
  15. handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
  16. i++;//等子线程接收到参数时主线程可能改变了这个i的值
  17. }
  18. //保证子线程已全部运行结束
  19. WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
  20. return 0;
  21. }
  22. unsigned int __stdcall Fun(void *pPM)
  23. {
  24. //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来
  25. int nThreadNum = *(int *)pPM; //子线程获取参数
  26. Sleep(50);//some work should to do
  27. g_nNum++;  //处理全局资源
  28. Sleep(0);//some work should to do
  29. printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum);
  30. return 0;
  31. }

运行结果可以参考下列图示,强烈建议读者亲自试一试。

图1

图2

图3

可以看出,运行结果完全是混乱和不可预知的。本系列将会运用Windows平台下各种手段包括关键段,事件,互斥量,信号量等等来解决这个问题并作一份全面的总结,敬请关注。

时间: 2024-10-30 14:56:13

转--秒杀多线程第四篇 一个经典的多线程同步问题的相关文章

秒杀多线程第四篇 一个经典的多线程同步问题

上一篇<秒杀多线程第三篇原子操作 Interlocked系列函数>中介绍了原子操作在多进程中的作用,如今来个复杂点的.这个问题涉及到线程的同步和相互排斥,是一道很有代表性的多线程同步问题,假设能将这个问题搞清楚,那么对多线程同步也就打下了良好的基础. 程序描写叙述: 主线程启动10个子线程并将表示子线程序号的变量地址作为參数传递给子线程.子线程接收參数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出參数和全局变量. 要求: 1.子线程输

[一个经典的多线程同步问题]总结

针对一个经典的线程同步互斥问题,前面几篇文章提出了四种解决方案:关键段.事件.互斥量.信号量. 下面对这四种解决方案做一个总结,梳理一下知识点: 首先来看下关于线程同步互斥的概念性的知识,相信大家通过前面的文章,已经对线程同步互斥有一定的认识了,也能模糊的说出线程同步互斥的各种概念性知识,下面再列出从<计算机操作系统>一书中选取的一些关于线程同步互斥的描述.相信先有个初步而模糊的印象再看下权威的定义,应该会记忆的特别深刻. 1.线程(进程)同步的主要任务 答:在引入多线程后,由于线程执行的异步

多线程第四篇秒杀 一个经典的多线程同步问题

前<秒杀多线程第三篇原子操作 Interlocked系列函数>中介绍了原子操作在多进程中的作用,如今来个复杂点的.这个问题涉及到线程的同步和相互排斥,是一道很有代表性的多线程同步问题,假设能将这个问题搞清楚,那么对多线程同步也就打下了良好的基础. 程序描写叙述: 主线程启动10个子线程并将表示子线程序号的变量地址作为參数传递给子线程. 子线程接收參数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出參数和全局变量. 要求: 1.子线程输出

一个经典的多线程同步问题

上一篇<秒杀多线程第三篇原子操作 Interlocked系列函数>中介绍了原子操作在多进程中的作用,现在来个复杂点的.这个问题涉及到线程的同步和互斥,是一道非常有代表性的多线程同步问题,如果能将这个问题搞清楚,那么对多线程同步也就打下了良好的基础. 程序描述: 主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程.子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量. 要求: 1.子线程输出的线

Java Tread多线程(0)一个简单的多线程实例

作者 : 卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39341887 本文演示,一个简单的多线程实例,并简单分析一下线程. 编程多线程时,一般步骤: 1)继承Thread函数. 2)覆盖run函数. 注意:1)main函数为主线程,main里面存放的是主线程的执行代码: Demo1为子线程,里面的run函数里面存放的是子线程需要执行的代码:其中,本文中主线程和子线程执行的优先级是一样的. 2)启动线程必须用start()启动,

秒杀多线程第五篇 经典线程同步 关键段CS

版权声明:本文为博主原创文章,未经博主允许不得转载. 上一篇<秒杀多线程第四篇 一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理. 关键段CRITICAL_SECTION一共就四个函数,使用很是方便.下面是这四个函数的原型和使用说明. 函数功能:初始化 函数原型: void InitializeCriticalSection(LPCRITIC

秒杀多线程第八篇 经典线程同步 信号量Semaphore

版权声明:本文为博主原创文章,未经博主允许不得转载. 阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event> <秒杀多线程第七篇经典线程同步互斥量Mutex> 前面介绍了关键段CS.事件Event.互斥量Mutex在经典线程同步问题中的使用.本篇介绍用信号量Semaphore来解决这个问题. 首先也来看看如何使用信号量,信号量Semaphore

转---秒杀多线程第五篇 经典线程同步 关键段CS

上一篇<秒杀多线程第四篇 一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理. 关键段CRITICAL_SECTION一共就四个函数,使用很是方便.下面是这四个函数的原型和使用说明. 函数功能:初始化 函数原型: void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection

转--- 秒杀多线程第六篇 经典线程同步 事件Event

阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题. 首先介绍下如何使用事件.事件Event实际上是个内核对象,它的使用非常方便.下面列出一些常用的函数. 第一个 CreateEvent 函数功能:创建事件 函数原型: HANDLEC