程序片段(01):01.一对一模式.c+02.中介者模式.c+03.广播模式.c

内容概要:事件

  1. ///01.一对一模式.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. //01.关于多线程:
  6. // (临界区+互斥量):线程冲突
  7. // (事件):线程通信
  8. // (时间):同步线程
  9. HANDLE eventArrA[2] = { 0 };
  10. HANDLE threadArrA[2] = { 0 };
  11. DWORD WINAPI haiHua(void * p)
  12. {
  13. printf("海华第01次说:i love you fangFang, please help me debug! \n");//信息通信内容
  14. Sleep(1000);//信息传递时间
  15. SetEvent(eventArrA[0]);//提示信息传到
  16. int i = 1;
  17. while (++i)
  18. {
  19. WaitForSingleObject(eventArrA[1], INFINITE);//等待信息传到
  20. printf("海华第%02d次说:i love you fangFang, please help me debug! \n", i);
  21. Sleep(1000);
  22. //ResetEvent(eventArrA[1]);//重置信息提示(手动)
  23. SetEvent(eventArrA[0]);
  24. }
  25. return 0;
  26. }
  27. DWORD WINAPI fangFang(void * p)
  28. {
  29. int i = 0;
  30. while (++i)
  31. {
  32. WaitForSingleObject(eventArrA[0], INFINITE);
  33. printf("王芳第%02d次说:sorry! but i love you! \n", i);
  34. Sleep(1000);
  35. SetEvent(eventArrA[1]);
  36. }
  37. return 0;
  38. }
  39. //02.关于CreateEvent(arg1, arg2, arg3, arg4);
  40. // arg1:安全属性集---->通常用NULL
  41. // arg2:手动重置事件-->手动:TRUE|自动:FALSE
  42. // 注:使用一次事件通知,用TRUE,使用多次事件通知,用FALSE
  43. // 注:使用一次线程通信,通常用的是信号量机制,而不是事件机制
  44. // arg3:事件激活状态-->通常用FALSE
  45. // arg4:事件唯一名称-->自定义(便于检索指定事件)
  46. int main01(void)
  47. {
  48. eventArrA[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
  49. eventArrA[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
  50. threadArrA[0] = CreateThread(NULL, 0, haiHua, NULL, 0, NULL);
  51. threadArrA[1] = CreateThread(NULL, 0, fangFang, NULL, 0, NULL);
  52. WaitForMultipleObjects(2, threadArrA, TRUE, INFINITE);//维持多线程异步并发执行的状态
  53. system("");
  54. }
  55. //01.事件深入:
  56. // 1.事件用于线程通信
  57. // 2.事件的额外细节:三个案例
  58. // 双方通话---->三方通话---->一对多模式
  59. // (相亲) (媒婆)中介者 (广播)广播模式
  60. //02.了解一些问题:
  61. // (临界区+互斥+原子变量):线程冲突
  62. // 事件:线程通信
  63. // 时间:同步线程
  64. //03.什么是死锁?
  65. // 编写事件的时候最容易遇到死锁的事情!
  66. //04.现在我们需要几个信号量,而且这个信号量我们用什么来进行描述?
  67. // 时间通知+信号量
  68. //05.顺序不严密:
  69. // 1.等待信号之后,信号需要复原才行,否则会出现不正常的情况(信号错乱!)
  70. // 2.信号不同步和乱序的解决方案-收到信号之后进行复位
  71. // (1).信号复位情况--必须复位,某些情况之下自动复位,建议主动复位
  72. // (2).围绕信号:的False&TRUE的分别
  73. // 第二个参数:密切相关-自动&手动[复位情况]
  1. ///02.中介者模式.c
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <Windows.h>
  6. HANDLE threadArrB[3] = { 0 };
  7. HANDLE eventArrB[4] = { 0 };
  8. char strB[256] = { 0 };//线程通信内容
  9. //01.三方通话:中介者设计模式
  10. // 海华向中介者:发出事件通知0
  11. // 中介者等海华:等待事件通知0
  12. // 中介者向芳芳:发出事件通知1
  13. // 芳芳等中介者:等待事件通知1
  14. // 芳芳向中介者:发出事件通知2
  15. // 中介者等芳芳:等待事件通知2
  16. // 中介者向海华:发出事件通知3
  17. // 海华等中介者:等待事件通知3
  18. // 海华向中介者:发出事件通知0
  19. DWORD WINAPI haiHuaB(void * p)
  20. {
  21. sprintf(strB, "海华第01次说:i love you fangFang, please help me debug! \n");//发出通信内容
  22. Sleep(1000);//模拟通信时间
  23. SetEvent(eventArrB[0]);//提示通信到达
  24. int i = 1;
  25. while (++i)
  26. {
  27. WaitForSingleObject(eventArrB[3], INFINITE);
  28. memset(strB, '\0', 256);
  29. sprintf(strB, "海华第%02d次说:i love you fangFang, please help me debug! \n", i);
  30. Sleep(1000);
  31. SetEvent(eventArrB[0]);
  32. }
  33. return 0;
  34. }
  35. DWORD WINAPI ruiFuB(void * p)
  36. {
  37. int i = 0;
  38. int status = 0;//切换通信对象
  39. while (++i)
  40. {
  41. if (!status)
  42. {
  43. WaitForSingleObject(eventArrB[0], INFINITE);
  44. printf("媒婆传递海华通信内容(传递次数:%02d): \n", i);
  45. printf("\t%s \n", strB);
  46. Sleep(1000);
  47. SetEvent(eventArrB[1]);
  48. status = 1;
  49. }
  50. else
  51. {
  52. WaitForSingleObject(eventArrB[2], INFINITE);
  53. printf("媒婆传递芳芳通信内容(传递次数:%02d): \n", i);
  54. printf("\t%s \n", strB);
  55. Sleep(1000);
  56. SetEvent(eventArrB[3]);
  57. status = 0;
  58. }
  59. }
  60. return 0;
  61. }
  62. DWORD WINAPI fangFangB(void * p)
  63. {
  64. int i = 0;
  65. while (++i)
  66. {
  67. WaitForSingleObject(eventArrB[1], INFINITE);
  68. memset(strB, '\0', 256);
  69. sprintf(strB, "王芳第%02d次说:sorry! but i love you! \n", i);
  70. Sleep(1000);
  71. SetEvent(eventArrB[2]);
  72. }
  73. return 0;
  74. }
  75. int main02(void)
  76. {
  77. eventArrB[0] = CreateEvent(NULL, FALSE, FALSE, L"haiHua");
  78. eventArrB[1] = CreateEvent(NULL, FALSE, FALSE, L"ruiFuToFang");
  79. eventArrB[2] = CreateEvent(NULL, FALSE, FALSE, L"fangFang");
  80. eventArrB[3] = CreateEvent(NULL, FALSE, FALSE, L"ruiFuToHua");
  81. threadArrB[0] = CreateThread(NULL, 0, haiHuaB, NULL, 0, NULL);
  82. threadArrB[1] = CreateThread(NULL, 0, ruiFuB, NULL, 0, NULL);
  83. threadArrB[2] = CreateThread(NULL, 0, fangFangB, NULL, 0, NULL);
  84. WaitForMultipleObjects(3, threadArrB, TRUE, INFINITE);
  85. system("pause");
  86. }
  87. //01.中介者模式&广播模式&图论模式[多对多]
  88. // 中介者:三方
  89. // 广播:一对多
  90. // 图论:多对多
  91. //注:多线程这块儿必须要会树和图
  92. //02.一对多的情况下:
  93. // 既可以采用数组进行管理也可以采用链表进行管理
  94. //03.涉及到树的情况之下:
  95. // QQ群聊天儿多线程,练就数据结构
  96. //04.一对多&多对多:
  97. // 群聊原理:中介者-->每个人进行转发
  98. // 中介者进行转发原理-->数组管理-->数组广播-->群聊模式
  99. //05.流程梳理:
  100. // 1.我发送一条消息,中介者收到之后,他进行群发动作
  101. // 2.中介者的衍生模式-->形成闭环-->选好起始点
  102. //06.编程思想:精髓
  103. // 原则上一个消息全局变量读取特点
  104. // 相当于是一个公告栏,权限读取
  105. //07.操作:
  106. // 定义全局变量,便于读取全局变量的数据
  1. ///03.广播模式.c
  2. #define _CRT_SECURE_NO_WARNINGS
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <Windows.h>
  6. HANDLE threadArrC[10] = { 0 };
  7. HANDLE eventArrC[1] = { 0 };
  8. char strC[256] = { 0 };//线程通信内容
  9. char chrArr[10] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K' };//代表十个人
  10. //01.一个线程发出事件通知消息,多条线程监听该事件通知消息
  11. // 一对多的模式
  12. DWORD WINAPI haiHuaC(void * p)
  13. {
  14. char * pChr = (char *)p;
  15. printf("i am %c haiHua's gir friend! \n", *pChr);
  16. if ('A' == *pChr)
  17. {
  18. MessageBox(0, TEXT("1"), TEXT("1"), 0);//暂停通知线程
  19. sprintf(strC, "海华女友%c speak:xiaohuahua lovely! \n", *pChr);//消息通知内容
  20. SetEvent(eventArrC[0]);//发出事件通知
  21. MessageBox(0, TEXT("1"), TEXT("1"), 0);//暂停通知线程
  22. sprintf(strC, "海华女友%c speak:xiaohuahua lovely! \n", *pChr);//消息通知内容
  23. SetEvent(eventArrC[0]);//发出事件通知
  24. }
  25. int i = 0;
  26. while (++i)
  27. {
  28. WaitForSingleObject(eventArrC[0], INFINITE);//等待事件通知
  29. printf("haiHua's girl friend %c read %s! \n", pChr, strC);
  30. Sleep(1000);
  31. ResetEvent(eventArrC[0]);
  32. }
  33. return 0;
  34. }
  35. int main03(void)
  36. {
  37. eventArrC[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
  38. for (int i = 0; i < 10; ++i)
  39. {
  40. threadArrC[i] = CreateThread(NULL, 0, haiHuaC, &chrArr[i], 0, NULL);
  41. }
  42. WaitForMultipleObjects(10, threadArrC, TRUE, INFINITE);
  43. system("pause");
  44. }
  45. //01.中介者设计模式之广播模式:QQ群聊原理
  46. // 群聊-->数组-->链表-->环状-->局域网:环状结构[网络可靠性]
  47. //02.QQ的开发:不仅有发送和收取消息-->所以线程非常多
  48. // 信号对称-->需要进行手动进行事件的设置
  49. // 一对一:自动
  50. // 中介者:自动
  51. // 一对多:手动
  52. // 多对多:手动

程序片段(02):01.Semaphore.c+02.SemaphoreNew.c

内容概要:信号量

  1. ///01.Semaphore.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. //01.信号量:
  6. // 1.类似于空位的特点:
  7. // 2.空置多少个位置就可以容纳多少个并行线程执行
  8. //注:当空余的位置为0的时候,也就不能在另起线程执行任务了
  9. #define id L"haiHua"//信号ID
  10. #define MAX 3//空位数
  11. //02.多线程信号量(semaphore)通信:
  12. // 1.特点:打开一个信号量(等同于检索一个指定ID名称的信号量)
  13. // 2.格式:HANDLE mySema = OpenSemaphore(arg1, arg2, arg3);
  14. // arg1:信号量检索范围(SEMAPHORE_ALL_ACCESS)
  15. // arg2:继承特性
  16. // arg3:信号量检索名称(自定义名称,在固定范围内唯一标识信号量)
  17. // 3.刚打开信号量的时候:
  18. // 信号量的空位为0,也就是无法开启新的异步线程执行任务
  19. // 信号量的空位为N,也就是说此刻可以开启N条异步线程执行任务代码
  20. //注:空位为N,表示除开当前线程之外可以另起的异步线程个数
  21. DWORD WINAPI myWorker(void * p)
  22. {
  23. int * pInt = (int *)p;
  24. printf("worker:%d si running! \n", *pInt);
  25. HANDLE mySema = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, id);//指定范围检索指定名称的信号量
  26. if (mySema)//判断信号量是否存在
  27. {
  28. printf("worker:%d is waiting! \n", *pInt);//表示当前线程处于判定信号量状态
  29. Sleep(1000);
  30. if (WaitForSingleObject(mySema, INFINITE) == WAIT_OBJECT_0)//等待空位为0
  31. {//如果信号空位为0,也就是无法开启异步线程的情况
  32. printf("worker:%d is getting! \n", *pInt);//此时只有当前线程获得CPU执行权,其它线程无法获取CPU可执行权
  33. Sleep(3000);
  34. printf("worker:%d is leaving! \n", *pInt);
  35. ReleaseSemaphore(mySema, 1, NULL);//释放信号:只是打开一个空位,也就是可以开启另外一条异步线程进行执行了
  36. CloseHandle(mySema);//释放资源信号量资源
  37. }
  38. }
  39. return 1;
  40. }
  41. //03.创建信号量:
  42. // 1.特点:初始化信号量对象
  43. // 2.格式:HANDLE mySema = CreateSemaphore(arg1, arg2, arg3, arg4);
  44. // arg1:安全属性集
  45. // arg2:初始空位数
  46. // arg3:最大空位数
  47. // arg4:信号量名称
  48. int main01(void)
  49. {
  50. HANDLE mySema = CreateSemaphore(NULL, 0, MAX, id);
  51. HANDLE threadArr[10] = { 0 };
  52. for (int i = 0; i < 10; ++i)
  53. {//由于当前信号量为0,因此多条执行同一段儿代码的时候,需要判定能否通过
  54. threadArr[i] = CreateThread(NULL, 0, myWorker, threadArr + i, 0, NULL);
  55. }
  56. Sleep(5000);
  57. printf("激活状态! \n");
  58. ReleaseSemaphore(mySema, MAX, NULL);//释放信号量
  59. WaitForMultipleObjects(10, threadArr, TRUE, INFINITE);
  60. CloseHandle(mySema);
  61. system("pause");
  62. }
  63. //01.信号量:Semaphore
  64. // 1.量值:0-1-2-3
  65. // 2.使用一次进行一次减数,到了一定的数据之后,做一些指定操作
  66. // 当数据减到至0的时候,信号为0,在使用信号量的地方,处于停滞状态
  67. // 3.信号量还可以做一些其他的限定操作
  68. // 4.线程通信:用途
  69. // 5.具备等待机制
  70. //02.信号计数原理:
  71. // 1.两个按键入口,多个行李
  72. // 2.信号衰减原理:空位原理
  73. // 信号为0的时候,没有空位为0,通过判断信号的空位情况,决定是否让线程干活儿
  74. // 等待唤醒机制0与非0的区别(非0,线程可执行,0线程不可执行)
  75. //03.关卡原理:
  76. // 1.同时最多支持10000个人购票
  77. // 2.如果超过10000个人,就需要排队
  78. // 3.当10000个人购票完毕的时候,重新开启线程执行任务
  79. //04.原理:if 0 等下去
  80. // 1.同一个信号量
  81. // 2.10个线程
  82. // 3.状态判定:
  83. // 0-->10个等待
  84. // 5-->5个等待,5个执行
  85. // 执行一次,减去一次-->信号量衰减
  86. // 4.所有的线程都能够读取到该信号量
  87. // 多个线程占用资源:等待执行状态
  88. // 用完与没有用完(线程不可执行状态与线程可执行状态)
  89. //05.信号量完全解析:
  90. // 步骤一:
  91. // HANDLE hSEM=CreateSemaphore(NULL,0,MAX,id);
  92. // 创建一个信号量,信号量的最大值为MAX,如果等于0的情况之下,它就在这儿死等下去
  93. // 步骤二:
  94. // ReleaseSemaphore(mySema,MAX,NULL);//释放信号量,补充空位数量
  95. // 一旦将信号量设定为5就会开始进行执行
  96. //06.什么样儿的情况之下我们使用信号量?
  97. // 实现两个线程的死锁状态,设定为1这个信号量,进或者不进
  1. ///02.SemaphoreNew.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. int num = 0;
  6. //01.信号量的应用:
  7. // 1.排队执行机制
  8. // 让多个线程处于执行状态,让多个线程处于休眠状态
  9. // 2.实现线程互斥
  10. // 让一个线程处于执行状态,让其它所有线程处于等待状态
  11. DWORD WINAPI add(void * p)
  12. {
  13. HANDLE mySema = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, L"xiaobin");//打开信号量:
  14. if (mySema)
  15. {
  16. if (WAIT_OBJECT_0 == WaitForSingleObject(mySema, INFINITE))
  17. {//判断信号强弱(判断信号量的数目,也就是判断空位数目)
  18. for (int i = 0; i < 10000; ++i)
  19. {
  20. ++num;
  21. }
  22. ReleaseSemaphore(mySema, 1, NULL);
  23. CloseHandle(mySema);
  24. }
  25. }
  26. else
  27. {
  28. printf("打开信号量失败! \n");
  29. }
  30. }
  31. int main02(void)
  32. {
  33. //01.实现线程互斥的关键代码:
  34. // 最多只能有一个空位(最多只能同时有一条线程处于执行状态)
  35. HANDLE mySema = CreateSemaphore(NULL, 0, 1, L"xiaobin");
  36. HANDLE threadArr[64] = { 0 };
  37. for (int i = 0; i < 64; ++i)
  38. {
  39. threadArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
  40. }
  41. printf("激活线程");
  42. ReleaseSemaphore(mySema, 1, NULL);
  43. WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
  44. printf("num = %d \n", num);
  45. CloseHandle(mySema);
  46. system("pause");
  47. }
  48. //01.信号量可以实现多个线程的卡顿现象
  49. //02.信号量的空位原理-->0和1的原理:互斥特点
  50. // 入职与离职原理的特点-->空位原理
  51. //03.如何使用信号量实现一个全局变量的自增?
  52. // 互斥类型的方式实现-->使用信号量实现线程之间的互斥现象
  53. //04.随机数的求取方法:
  54. // 1.原始函数
  55. // 2.多线程的数据丢失
  56. //05.多线程处理状态下的CPU是不会稳定的情况
  57. //06.信号量:0代表没有空位-->线程等待状况
  58. // 控制访问次数
  59. //07.互斥量与信号量的区别:
  60. // 互斥量:只能让一个线程处于运行状态
  61. // 信号量:可以让多个线程处于运行状态,其他线程休眠
  62. // 临界区:只能让一个线程处于运行状态
  63. // 时间同步:
  64. // 事件:也可以实现让多个线程处于运行状态,其他线程休眠状态
  65. // 原子操作:操作速度最快,因为它不需要等待操作线程,几乎直接运行状态
  66. //注:原子量的速度快在于无需让多条线程处于等待执行状态,其他线程互斥的方式
  67. // 存在着线程等待执行的状态

程序片段(03):01.互斥.c+02.互斥读写.c

内容概要:互斥锁

  1. ///01.互斥.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. int static num = 6400000;
  6. //01.全局写入锁
  7. // 当一个线程在进行指定变量的写入操作的时候;
  8. // 其它线程若是需要写入这个指定变量数据,那么
  9. // 其他线程只能处于等待执行写入数据的状态
  10. SRWLOCK gw_lock = { 0 };
  11. static DWORD WINAPI write(void * p)
  12. {//多线程写入状态下,同一时刻只能由一条线程执行写入状态!
  13. AcquireSRWLockExclusive(&gw_lock);//获得独立写入锁(进入锁定状态)
  14. for (int i = 0; i < 100000; ++i)
  15. {
  16. --num;
  17. }
  18. ReleaseSRWLockExclusive(&gw_lock);//释放独立写入锁(释放锁定状态)
  19. return 0;
  20. }
  21. int main01(void)
  22. {
  23. InitializeSRWLock(&gw_lock);
  24. HANDLE threadArr[64];
  25. for (int i = 0; i < 64; ++i)
  26. {
  27. threadArr[i] = CreateThread(NULL, 0, write, NULL, 0, NULL);
  28. }
  29. //num += 10000;//没有生效,说明互斥锁的原则是全局生效,是对所有线程生效!
  30. WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
  31. printf("num = %d \n", num);
  32. system("pause");
  33. }
  34. //01.互斥锁的概念:互斥
  35. // 如同交往一个女友之后就被锁定了
  36. //02.互斥锁问题:
  37. // 线程互斥:同一时刻,只能由同一个线程执行数据操作
  38. //03.线程的互斥实现方式:
  39. // 临界区-->互斥量-->原子操作-->事件-->信号量-->互斥锁
  40. //04.互斥锁的创建流程:
  41. // 全局变量:定义互斥量
  42. // SRWLOCK g_lock;
  43. // Main函数:初始化互斥量
  44. // InitializeSRWLock(&g_lock);
  45. // 数据锁定:写入和读取的锁定
  46. // 线程函数:
  47. // AcquireSRWLockExclusive(&g_lock);//锁定写入
  48. // ReleaseSRWLockExclusive(&g_lock);//锁定释放
  49. //05.互斥锁的使用场景:
  50. // 1.改变一个变量的时候需要锁定(防止同时读取,同时写入)
  51. // 2.读取一个变量的时候也需要锁定(防止同时写入,同时读取)
  1. ///02.互斥读写.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. int num = 6400000;//待读写的数据
  6. SRWLOCK g_lock = { 0 };//写入锁
  7. DWORD WINAPI write(void * p)
  8. {
  9. printf("开始写入! \n");
  10. AcquireSRWLockExclusive(&g_lock);
  11. for (int i = 0; i < 100000; ++i)
  12. {
  13. --num;
  14. //Sleep(10);
  15. }
  16. ReleaseSRWLockExclusive(&g_lock);
  17. printf("结束写入! \n");
  18. return 0;
  19. }
  20. DWORD WINAPI read(void * p)
  21. {
  22. printf("读取状态! \n");
  23. AcquireSRWLockShared(&g_lock);
  24. int i = 0;
  25. while (1)
  26. {
  27. ++i;
  28. Sleep(1000);
  29. printf("第%d秒, num = %d \n", i, num);//由于写入状态锁定了,因此这里的读取状态,无法读取到数据
  30. if (20 == i)
  31. break;
  32. }
  33. ReleaseSRWLockShared(&g_lock);
  34. printf("读取结束! \n");
  35. return 0;
  36. }
  37. int main02(void)
  38. {
  39. InitializeSRWLock(&g_lock);
  40. CreateThread(NULL, 0, read, NULL, 0, NULL);
  41. HANDLE threadArr[64] = { 0 };
  42. for (int i = 0; i < 64; ++i)
  43. {
  44. threadArr[i] = CreateThread(NULL, 0, write, NULL, 0, NULL);
  45. }
  46. WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
  47. printf("num = %d \n", num);
  48. system("pause");
  49. }
  50. //01.互斥锁的读写状态控制
  51. // 写入的状态下不可读取,读取的状态下不可写入
  52. //02.锁定状态,读取完成之后才进行锁定
  53. //03.一个资源只能锁定一次,不能锁定多次
  54. //04.锁定-->防止冲突问题-->读取和写入的状态
  55. // 防止同时写入和读取数据

程序片段(04):Mutex.c

内容概要:01.跨进程Mutex(发互斥)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. int main(void)
  6. {
  7. //01.创建互斥量"Mutex":
  8. // 位于内核层|Ring0层
  9. HANDLE myMutex = CreateMutexA(NULL, TRUE, name);
  10. printf("在内核层|Ring0层创建互斥量(Mutex)成功! \n");
  11. char chr = getch();//实时获取单个字符
  12. //02.释放互斥量:
  13. // 相当于发出通知
  14. ReleaseMutex(myMutex);
  15. //03.关闭互斥量:
  16. CloseHandle(myMutex);
  17. system("pause");
  18. }
  19. //01.关于跨进程的驱动访问:内核对象
  20. // 无论是Windows还是Linux都是存在互斥量说法
  21. //02.如果是跨进程的话:
  22. // 创建跨进程的Mutext需要有名称(便于全局访问)
  23. //03.编写网络程序的时候:
  24. // 既需要编写客户端也需要编写网络端
  25. // -->编写两个程序的时代
  26. //04.演示的时候需要:
  27. // 进行编译好的程序之间的演示
  28. //05.跨进程通信:
  29. // 1.Event&Mutex&semaphore都可以实现跨进程的线程通信
  30. // 2.Mutex是最安全的跨进程线程访问(因为能够处理发送通知方的断开情况)
  31. // 发出通信信息的进程退出情况能够处理!

程序片段(05):Mutex.c

内容概要:02.跨进程Mutex(收互斥)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. int main(void)
  6. {
  7. //01.打开互斥量:
  8. // 作用:获取互斥量
  9. // 格式:HANDLE mutex = OpenMutexA(arg1, arg2, arg3);
  10. // mutex:互斥量+arg1:检索范围+arg2:继承句柄+互斥量名称
  11. HANDLE myMutex = OpenMutexA(MUTEX_ALL_ACCESS, TRUE, name);//获取互斥量
  12. //if (NULL == myMutex)//判定互斥量
  13. //{
  14. // printf("获取互斥量失败! \n");
  15. // system("pause");
  16. // return;
  17. //}
  18. printf("等待状态! \n");
  19. //02.控制互斥量的等待时间:
  20. // 先获得互斥量-->设定等待状态时间(等待指定的时间范围!)
  21. DWORD res = WaitForSingleObject(myMutex, 10000);//设置等待状态
  22. switch (res)
  23. {
  24. case WAIT_OBJECT_0:
  25. printf("收到跨进程信号! \n");
  26. break;
  27. case WAIT_TIMEOUT:
  28. printf("等待跨进程信号超时! \n");
  29. break;
  30. case WAIT_ABANDONED:
  31. printf("另外一个进程发生终止!结束跨进程信号等待状态! \n");
  32. break;
  33. default:
  34. break;
  35. }
  36. CloseHandle(myMutex);
  37. system("pause");
  38. }
  39. //01.刚才的程序特点:
  40. // 都是出于同一个进程内的线程操作(同一进程)
  41. //02.C++关于"事件"和"信号量"的封装:
  42. // 封装通用的一个机制,Cocos2dx的时候都是一样的情况
  43. // 包含OC也一样,只不过它们将接口内容进行了简化
  44. //03.多线程的强化:
  45. // 1.event&mutex&semaphore:(驱动层|Ring0层)
  46. // 本质:是处于驱动里面的一个综合信号量
  47. // 2.操作系统起到什么作用?
  48. // (1).操作系统类似于一个巨大的进程,里面运行的每个程序类似于线程
  49. // (类比:大进程&进程)<--->(进程&线程)
  50. // (2).电脑重启,打开多个.exe都需要重启
  51. // (3).操作系统和应用程序之间的关系就如同进程和线程之间的关系
  52. // (4).高级机制:内核对象(Ring0层对象)
  53. //04.操作系统的高级机制:内核对象-->项目使用-->跨进程使用
  54. // 1.操作系统的分层机制:
  55. // (1).ring0:就是最底层,这里可以用于编写驱动(出错:蓝屏)
  56. // (2).ring3:就是应用层,(出错:进程出错)
  57. // 2.线程互斥区分机制:
  58. // (1).event&mutex:
  59. // 这里创建的指针处于应用层,但是指针所指向的内存处于ring0层
  60. // ring0层当中的对象可以看到所有进程的内存(最高访问权限)
  61. // (2).进程之间不可以进行相互读写,必须通过注射方式
  62. // (3).event&mutex都是出于ring0层的内核对象
  63. // 本质:对象的底层特点
  64. // 所以:它们不仅可以用于一个进程内的线程互斥,还可以用于多个进程之间的线程互斥
  65. //05.mutex的互斥问题解析:
  66. // 1.跨进程的mutex互斥问题
  67. // 2.C++的线程库都是对C语言多线程的封装
  68. // 大概原理-->C++的类使用
  69. //06.关于跨进程通信的问题:
  70. // 最好使用互斥量(mutex)实现跨进程通信
  71. // 原因:其他方式(event&semaphore)不能处理进程断开的情况!

程序片段(06):发事件.c

内容概要:01.跨进程Event(发事件)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. //01.Event实现跨进程通信:
  6. // 1.这儿的Event不是线程级别的含义,而是进程级别的含义:
  7. // 该Event实质上是位于(内核|Ring0层),因此可以实现跨进程通信
  8. // 2.参数说明:第二个参数表示是否重置手动重置事件状态
  9. // TRUE:手动重置+FALSE:自动重置
  10. int main(void)
  11. {
  12. HANDLE myEvent = CreateEventA(NULL, FALSE, FALSE, name);//创建事件
  13. printf("跨进程Event创建成功! \n");
  14. char chr = getch();
  15. SetEvent(myEvent);//设置事件
  16. printf("发送跨进程Event事件! \n");
  17. CloseHandle(myEvent);
  18. system("pause");
  19. }
  20. //01.严格区分跨线程和跨进程
  21. //02.使用Event实现跨进程线程访问
  22. //03.Event和Mutex有一定的区别:
  23. // Event跨进程不能使用匿名的,否则的话找不到
  24. //注:跨进程一定要采用唯一名称标识信号
  25. //04.TCP/UDP的时候就是如此复杂的情况
  26. //05.一般进程与进程之间都需要设定一个超时等待时间
  27. //06.Event天生的缺陷:
  28. // 只有Mutex可以感知到另外一个进程的丢失
  29. // Event不具备感知进程丢失的功能
  30. //注:进程通信情况之下的进程丢失情况分析!

程序片段(07):收事件.c

内容概要:02.跨进程Event(收事件)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. int main(void)
  6. {
  7. //01.打开事件:
  8. // 获取跨进程所创建的事件
  9. HANDLE myEvent = OpenEventA(EVENT_ALL_ACCESS, TRUE, name);//获取事件
  10. if (NULL == myEvent)
  11. {
  12. printf("跨进程Event获取失败! \n");
  13. system("pause");
  14. return;
  15. }
  16. printf("跨进程Event等待状态! \n");
  17. DWORD res = WaitForSingleObject(myEvent, 10000);
  18. switch (res)
  19. {
  20. case WAIT_OBJECT_0:
  21. printf("跨进程Event收到状态! \n");
  22. break;
  23. case WAIT_TIMEOUT:
  24. printf("跨进程Event超时状态! \n");
  25. break;
  26. case WAIT_ABANDONED:
  27. printf("另外一个进程已经中止! \n");
  28. break;
  29. default:
  30. break;
  31. }
  32. CloseHandle(myEvent);
  33. system("pause");
  34. }

程序片段(08):发信号.c

内容概要:01.跨进程Semaphore(发信号)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. int main(void)
  6. {
  7. HANDLE mySema = CreateSemaphoreA(NULL, 0, 1, name);
  8. printf("跨进程Semaphore信号量创建成功! \n");
  9. char chr = getch();
  10. ReleaseSemaphore(mySema, 1, NULL);
  11. printf("跨进程Semaphore发出信号! \n");
  12. CloseHandle(mySema);
  13. system("pause");
  14. }
  15. //01.当一条线程做完一件事情之后,需要通知其他线程的时候:
  16. // 这个时候就需要进行线程之间的通信
  17. //注:区分线程通信与进程通信
  18. //02.大数据你就得将图论和树结构玩儿的相当好才行
  19. // 图和树就是用于管理这么多的线程的
  20. //03.线程与线程之间的关系是很复杂的:
  21. // 需要掌握逻辑&排序&容错&模糊
  22. //04.跨进程的线程通信:
  23. // Event&Mutex&Semaphore
  24. //05.使用跨进程通信的时候:
  25. // 1.最佳解决方案就是Mutex
  26. // 2.缺点比较:
  27. // Event&Semaphore:发信信号的进程关闭之后无法感知到!
  28. // Mutex:发送信号的进程关闭之后能够被感知到!

程序片段(09):收信号.c

内容概要:02.跨进程Semaphore(收信号)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. char name[100] = "haihualovefang";
  5. int main(void)
  6. {
  7. HANDLE mySema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, name);
  8. if (NULL == mySema)
  9. {
  10. printf("跨进程Semaphore创建失败! \n");
  11. system("pause");
  12. return;
  13. }
  14. printf("跨进程Semaphore等待状态! \n");
  15. DWORD res = WaitForSingleObject(mySema, 10000);
  16. switch (res)
  17. {
  18. case WAIT_OBJECT_0:
  19. printf("跨进程Semaphore通信收到! \n");
  20. break;
  21. case WAIT_TIMEOUT:
  22. printf("跨进程Semaphore通信超时! \n");
  23. break;
  24. case WAIT_ABANDONED:
  25. printf("另外一个进程已经中止! \n");
  26. break;
  27. default:
  28. break;
  29. }
  30. CloseHandle(mySema);
  31. system("pause");
  32. }

程序片段(10):TimePrc.c

内容概要:时间同步

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <Windows.h>
  4. //01.时间同步:标准回调函数格式
  5. // 1.小写"void"和大写"VOID"实质一样-->在这儿只是回调函数的规范
  6. // 2.参数:普通指针+时间1[低点]+时间2[高点]-->相当于时差
  7. // 3.创建一个回调函数格式的函数指针常量
  8. // 4.回调函数:CALLBACK的标识定义(标准定义)
  9. VOID CALLBACK timeRun(void * pArg, DWORD timeLow, DWORD timeHigh)
  10. {
  11. DWORD dwindex = *(DWORD *)pArg;
  12. printf("第%d次! \n", dwindex);
  13. MessageBoxA(0, "1", "2", 0);
  14. }
  15. //02.Win操作系统之下使用系统自带的定时器资源:
  16. // 1.创建定时器:有几个函数-->起到等待作用的定时器
  17. // 2.参数:arg1,arg2,arg3-->arg3是定时器的名称
  18. // 3.匿名定时器只能有一个,携带名称的定时器可以有多个!
  19. int main(void)
  20. {
  21. HANDLE time1 = CreateWaitableTimerA(NULL, TRUE, "haihua");
  22. if (NULL == time1)
  23. {
  24. printf("定时器创建失败! \n");
  25. }
  26. //设置定时器特点
  27. LARGE_INTEGER myTime;
  28. myTime.QuadPart = -50000000;//单位:0.1微妙--万分之一毫秒
  29. //SetWaitTimer:定义解释
  30. // _In_ HANDLE hTimer;定时器
  31. // _In_ const LARGE_INTEGER * 1pDueTime;//时间
  32. // _In_ LONG 1Period;//循环次数
  33. // _In_opt_ PTIMERAPCROUTINE pfnCompletionRoutine;//函数指针
  34. // _In_opt_ LPVOID 1pArgToCompletionRoutline;//参数
  35. // _In_ BOOL fResume;//始终恢复状态
  36. //设置等待的定时器(等待定时器)
  37. DWORD dwparam = 1;
  38. //1000说明1000毫秒-->1分钟干一次,回调间隔
  39. if (SetWaitableTimer(time1, &myTime, 1000, timeRun, &dwparam, FALSE))
  40. {//五秒钟之后触发该事件:1|0
  41. printf("等待5秒之后开始干活儿! \n");
  42. for (int i = 0; i < 15; ++i, ++dwparam)
  43. {//执行次数-->循环多少次,就回调多少次
  44. SleepEx(INFINITE, TRUE);
  45. }
  46. }
  47. //循环完毕之后所需执行的操作:
  48. // 取消定时器和关闭句柄资源
  49. CancelWaitableTimer(time1);
  50. CloseHandle(time1);
  51. if (WAIT_OBJECT_0 == WaitForSingleObject(time1, INFINITE))
  52. {//等待消息成功
  53. printf("wait ok! \n");
  54. }
  55. else
  56. {
  57. printf("wait no! \n");
  58. }
  59. system("pause");
  60. }
  61. //01.多线程与队列:
  62. // 实现文件加密
  63. //02.关于"时间定时器"的一些操作:
  64. // 简单定时器-->允许回调函数
  65. //03.时间同步问题:
  66. // 1.主要用于解决多个线程的时间问题[多线程]
  67. // 2.围绕时间定时器,每隔一段事件干一定的活儿
  68. // 3.满足一定的时间条件,然后解决一定的问题
  69. //04.回调函数与时间的概念:
  70. // 1.触发函数的动作-->回调动作
  71. // 2.执行完一段代码之后,执行某一个函数
  72. //05.回调函数原理:
  73. // 1.数据1,2-->根据符号进行运算
  74. // 2.整体函数[参数1+参数2+函数指针]
  75. // 3.定时器触发一个函数的执行
  76. // 4.多个线程在同一时间要干某些事件
  77. //06.同一个事件通知多个事件的执行
  78. //07.回调函数:Callback
  79. // 1.函数指针,可以进行调用-->实现任何代码都可以进行自定义
  80. // 2.整合功能:自定义功能以及它定义功能
  81. //08.函数指针:新的功能-->函数指针-->功能随时更换
  82. // 1.百度搜索原理
  83. // 2.百度后台的搜索算法的改变
  84. // 3.用户调用的时候会根据函数指针的区别[付钱状态,区域]
  85. //09.创建多个定时器需要根据名称进行区分
  86. //10.定时器的使用步骤:
  87. // 1.创建定时器:
  88. // HANDLE time1=CreateWaitableTimerA(NULL,TRUE,"haihua");
  89. // 2.五秒钟之后启动定时器:
  90. // LARGE_INTEGER mytime;
  91. // mytime.QuadPart=-50000000;
  92. // 3.定时器回调函数:
  93. // if(SetWaitableTimer(time1,&mytime,3000,timerun,&dwparam,FALSE)){}
  94. // 回调周期:3000毫秒之后循环一次-->循环多少次
  95. //11.时间同步:主要用于游戏的开发
  96. // 内核对象:游戏开发-->为了时间而单独编写一条线程不划算
  97. // CreateWaitableTimerA();-->内核对象
  98. // SetWaitableTimer();-->内核对象
  99. // 内核对象-->多个时钟都有一个名称-->我就可以让多个线程同时读取一个时钟,进行操作,避免耗费资源

程序片段(11):volatile.c

内容概要:Volatile

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <limits.h>
  4. void main01()
  5. {
  6. for (volatile int i = 0; i < INT_MAX; i++)
  7. {//(1)区分Debug模式和Release[+]模式
  8. //(2)优化:强制读取内存
  9. }
  10. system("pause");
  11. }
  12. void main02()
  13. {
  14. volatile int i = 10;
  15. int a = i;
  16. printf("\ni=%d", a);
  17. //{偷偷改变i的值}
  18. _asm
  19. {//(1)_asm是汇编语言
  20. //(2)修改数据-->栈底的内存是ebp,这里让其-4,也就是i这个栈底数据的改变
  21. //(3)16进制的20h=32
  22. //(4)这里的i已经不再寄存器里面,所以volatile强制读取内存当中经过修改之后的数据
  23. //(5)ebp-4相当于栈底的指针:直接修改数据
  24. mov dword ptr[ebp - 4], 20h;
  25. }
  26. int b = i;
  27. printf("\ni=%d", b);
  28. getchar();
  29. }
  30. //01.Volatile强化:
  31. // 主要解决强制读取内存的动作
  32. //02.Volatile原理:寄存器-内存
  33. // 1.寄存器读取i的值,然后赋值给a,b
  34. // 2.当检测到i没有被修改的时候,读取寄存器中的i值
  35. // 3.写入到a,b当中-->于是就缺少了读取内存的一次
  36. // 4.只是读取了一次内存当中的i值
  37. //03.数据被意外改变的情况之下经常使用Volatile
  38. // 数据意外改变-->编译器优化-->不读取内存[失误]

程序片段(12):Queue.h+Queue.c+数组顺序队列.c

内容概要:01.数组顺序队列

  1. ///Queue.h
  2. #pragma once
  3. #define DT int
  4. #define EN 100
  5. typedef struct queue
  6. {
  7. int head;
  8. int tail;
  9. DT arr[EN];
  10. } Queue;
  11. void initQueue(Queue * pQueue);
  12. int queueIsFull(Queue * pQueue);
  13. void enQueue(Queue * pQueue, DT data);
  14. int queueIsEmpty(Queue * pQueue);
  15. void showQueue(Queue * pQueue);
  16. DT queuGetHead(Queue * pQueue);
  17. void deQueue(Queue * pQueue);
  1. ///Queue.c
  2. #include "Queue.h"
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. void initQueue(Queue * pQueue)
  6. {
  7. if (NULL == pQueue)
  8. abort();
  9. memset((*pQueue).arr, 0, EN * sizeof(DT));
  10. (*pQueue).tail = (*pQueue).head = 0;
  11. }
  12. int queueIsFull(Queue * pQueue)
  13. {
  14. if (NULL == pQueue)
  15. abort();
  16. if (EN == (*pQueue).tail)
  17. return 1;
  18. return 0;
  19. }
  20. void enQueue(Queue * pQueue, DT data)
  21. {
  22. if (NULL == pQueue)
  23. abort();
  24. if (queueIsFull(pQueue))
  25. return;
  26. (*pQueue).arr[(*pQueue).tail] = data;
  27. ++(*pQueue).tail;
  28. }
  29. int queueIsEmpty(Queue * pQueue)
  30. {
  31. if (NULL == pQueue)
  32. abort();
  33. if ((*pQueue).head == (*pQueue).tail)
  34. return 1;
  35. return 0;
  36. }
  37. void showQueue(Queue * pQueue)
  38. {
  39. if (NULL == pQueue)
  40. abort();
  41. if (queueIsEmpty(pQueue))
  42. return;
  43. for (int i = 0; i < (*pQueue).tail; ++i)
  44. {
  45. printf("%3d", (*pQueue).arr[i]);
  46. }
  47. printf("\n");
  48. }
  49. DT queueGetHead(Queue * pQueue)
  50. {
  51. if (NULL == pQueue)
  52. abort();
  53. if (queueIsEmpty(pQueue))
  54. return;
  55. return (*pQueue).arr[0];
  56. }
  57. //01.几种特殊数据结构的实现方式:
  58. // 1.栈结构:
  59. // 数组栈:tail-=1(无所谓正向和反向)
  60. // 链表栈:
  61. // 正向:尾部增加,尾部减少
  62. // 反向:头部增加,头部减少
  63. // 2.队列结构:
  64. // 数组队列:正反向的效率一致
  65. // 链表队列:
  66. // 正向:尾部增加,头部减少
  67. // 反向:头部增加,尾部减少
  68. //注:数组队列,存在明显缺点,需要进行内存移动!
  69. // 队列的损耗,移动费时费力!
  70. //注:解决数组移动移动费时费力的方案:
  71. // 改造成环数组形队列+改造成链表队列
  72. void deQueue(Queue * pQueue)
  73. {
  74. if (NULL == pQueue)
  75. abort();
  76. if (queueIsEmpty(pQueue))
  77. return;
  78. for (int i = 0; i < (*pQueue).tail - 1; ++i)
  79. {
  80. (*pQueue).arr[i] = (*pQueue).arr[i + 1];
  81. }
  82. --(*pQueue).tail;
  83. }
  1. ///数组顺序队列.c
  2. #include "Queue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. int main01(void)
  6. {
  7. Queue queue;
  8. initQueue(&queue);
  9. for (int i = 0; i < 10; ++i)
  10. {
  11. enQueue(&queue, i);
  12. }
  13. showQueue(&queue);
  14. while (!queueIsEmpty(&queue))
  15. {
  16. printf("出队的数据是%3d \n", queueGetHead(&queue));
  17. deQueue(&queue);
  18. showQueue(&queue);
  19. }
  20. system("pause");
  21. }
  22. //01.顺序队列:逻辑编程
  23. // 工厂模式+(生产者-消费者)模式+请求响应模式
  24. //02.生产者与消费者:
  25. // 1.生产线程(生产者)
  26. // 2.消费线程(消费者)
  27. // 3.库存情况:库存越少越好,但是不能断掉供应链
  28. // 队列关系:生产者生产,消费者消费
  29. // 顺序关系:先进先出特点(存在顺序)
  30. // 原理:队列&多线程--请求|响应模式
  31. //03.三种队列的实现:
  32. // 1.数组顺序队列(尾部插入,头部取出)
  33. // 2.数组环形顺序队列(尾部插入,头部取出)
  34. // 2.链表反向队列(头部插入,尾部取出)
  35. // 队列实现:基础之上实现
  36. // (生产者&消费者)模式
  37. // (发送消息&接收消息)的模式
  38. // (请求&响应)模式
  39. //04.数组顺序队列-->数组环形顺序队列
  40. // 单链表-->双链表:单独的结构-->追求快一点儿,从简
  41. //05.环形队列原理:
  42. // 1.吃东西-->拉东西
  43. // 2.吃:前面+,拉:往前走
  44. // 3.吃的太多,重合情况(特殊情况)
  45. //06.环形队列解释:
  46. // 1.头尾重合,没有数据,添加一个数据之后,头不变,尾向后移一个结构体单位
  47. // 2.顺序队列的缺点:删除的时候移动很累(数组环形队列可以解决这个问题)
  48. //07.顺序队列解释:
  49. // 1.头部必须固定
  50. // 2.移动费时费力
  51. //08.队列移动问题的改造:
  52. // 1.链表结构
  53. // 2.环形队列

程序片段(13):CircleQueue.h+CircleQueue.c+数组正向环形队列.c

内容概要:02.数组环形顺序队列

  1. ///CircleQueue.h
  2. #pragma once
  3. #define DT int
  4. #define EN 0
  5. typedef struct circleQueue
  6. {
  7. int head;
  8. int tail;
  9. DT arr[EN];
  10. } CircleQueue;
  11. void initCircleQueue(CircleQueue * pCircleQueue);
  12. int circleQueueIsFull(CircleQueue * pCircleQueue);
  13. void enCircleQueue(CircleQueue * pCircleQueue, DT data);
  14. int circleQueueIsEmpty(CircleQueue * pCircleQueue);
  15. void showCircleQueue(CircleQueue * pCircleQueue);
  16. DT circleQueueGetHead(CircleQueue * pCircleQueue);
  17. void deCircleQueue(CircleQueue * pCircleQueue);
  1. ///CircleQueue.c
  2. #include "CircleQueue.h"
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. void initCircleQueue(CircleQueue * pCircleQueue)
  6. {
  7. if (NULL == pCircleQueue)
  8. abort();
  9. memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
  10. (*pCircleQueue).tail = (*pCircleQueue).head = 0;
  11. }
  12. //01.如何判断环形队列是否装满元素?
  13. // 1.这儿有三种特殊情况需要考虑:
  14. // 头部+中部+尾部
  15. // 2.最终可归结为两种环形队列满元素的情况:
  16. // 头部+中部(尾部和头部合并)
  17. // 3.归纳总结:
  18. // 头尾+循环情况
  19. int circleQueueIsFull(CircleQueue * pCircleQueue)
  20. {
  21. if (NULL == pCircleQueue)
  22. abort();
  23. if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)
  24. {
  25. return 1;
  26. }
  27. return 0;
  28. }
  29. void enCircleQueue(CircleQueue * pCircleQueue, DT data)
  30. {
  31. if (NULL == pCircleQueue)
  32. abort();
  33. if (circleQueueIsFull(pCircleQueue))
  34. return;
  35. (*pCircleQueue).arr[(*pCircleQueue).tail] = data;
  36. (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;
  37. }
  38. int circleQueueIsEmpty(CircleQueue * pCircleQueue)
  39. {
  40. if (NULL == pCircleQueue)
  41. abort();
  42. if ((*pCircleQueue).head == (*pCircleQueue).tail)
  43. return 1;
  44. return 0;
  45. }
  1. ///数组正向环形队列.c
  2. //01.数组顺序环形队列的思想:
  3. // 1.就是把数组当成闭合顺序环形队列(数组-->抽象-->环形队列)
  4. // 2.思想演示:
  5. // (1).原型:1 2 3 4 5 6 7 8 9 10
  6. // rear front
  7. // (2).抽离:8 9 10 1 2
  8. // 原理:普通数组抽象为顺序环形队列
  9. // 1).front-->rear:两个指针轮询移动
  10. // 2).防止front和rear:都走到头的情况
  11. // 3).节约移动情况(环形队列的优点)
  12. //02.环形队列实现:
  13. // 数组法+链表法
  14. // 顺序法+逆序法
  15. //03.环形队列的应用场景:
  16. // 操作系统对线程的管理这块儿
  17. //04.环形队列的两种情况:
  18. // 头尾情况+中部情况
  19. //05.环形队列:情况分析
  20. // 头尾+中部最终利用一个表达式进行表示
  21. //06.环形链表:
  22. // 1.rear说明了元素的个数
  23. // front=0&rear=5的情况
  24. // 2.rear重合情况二
  25. // 3.一般情况之下,要是想实现环形队列,数组或者链表都
  26. // 需要空出一个位置,防止front&rear重合
  27. //07.环形链表规则指定:
  28. // 1.为空:避免重合和满了的情况一致
  29. // 2.rear+1%5的特点-->代表存储继续前进
  30. // 3.满的情况综合:
  31. // (rear+1)%5==front说明重合装满

程序片段(15):CircleQueue.h+CircleQueue.h+数组正向环形队列.c

内容概要:01.数组正向环形队列

  1. ///CircleQueue.h
  2. #pragma once
  3. #define DT int
  4. #define EN 10
  5. //01.数组正向环形队列:
  6. // 优点:出队一个元素,无需进行队列数组元素的整体移动
  7. // 特点:如果模拟数组的长度为N
  8. // 普通队列:需要使用到N个元素
  9. // 环形队列:需要使用到N-1个元素
  10. //注:留出一个空位是为了区分队列重合情况和队列满载情况
  11. // 普通重合情况:就是空队列
  12. // 特殊重合情况:就是满队列
  13. typedef struct circleQueue
  14. {
  15. DT arr[EN];
  16. int head;
  17. int tail;
  18. }CircleQueue;
  19. void initCircleQueue(CircleQueue * pCircleQueue);
  20. int circleQueueIsFull(CircleQueue * pCircleQueue);
  21. void enCircleQueue(CircleQueue * pCircleQueue, DT data);
  22. int circleQueueIsEmpty(CircleQueue * pCircleQueue);
  23. void showCircleQueue(CircleQueue * pCircleQueue);
  24. DT circleQueueGetHead(CircleQueue * pCircleQueue);
  25. void deCircleQueue(CircleQueue * pCircleQueue);
  1. ///CircleQueue.c
  2. #include "CircleQueue.h"
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. #include <stdio.h>
  6. void initCircleQueue(CircleQueue * pCircleQueue)
  7. {
  8. if (NULL == pCircleQueue)
  9. abort();
  10. memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
  11. (*pCircleQueue).tail = (*pCircleQueue).head = 0;
  12. }
  13. int circleQueueIsFull(CircleQueue * pCircleQueue)
  14. {
  15. if (NULL == pCircleQueue)
  16. abort();
  17. if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)
  18. return 1;
  19. return 0;
  20. }
  21. //01.空位主要的作用:
  22. // 1.为了缓冲末尾位置可以进行循环填充数据!
  23. // 2.为了可以准确区分环形队列的两种情况:
  24. // 空队列+满队列
  25. //注:还可以确定最后一个入队的元素到底应当放置于何处!
  26. void enCircleQueue(CircleQueue * pCircleQueue, DT data)
  27. {
  28. if (NULL == pCircleQueue)
  29. abort();
  30. if (circleQueueIsFull(pCircleQueue))
  31. return;
  32. (*pCircleQueue).arr[(*pCircleQueue).tail] = data;
  33. (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;
  34. }
  35. int circleQueueIsEmpty(CircleQueue * pCircleQueue)
  36. {
  37. if (NULL == pCircleQueue)
  38. abort();
  39. if ((*pCircleQueue).head == (*pCircleQueue).tail)
  40. return 1;
  41. return 0;
  42. }
  43. void showCircleQueue(CircleQueue * pCircleQueue)
  44. {
  45. if (NULL == pCircleQueue)
  46. abort();
  47. if (circleQueueIsEmpty(pCircleQueue))
  48. return;
  49. //环形队列:元素不确定+起点不确定(无法确定循环次数)
  50. int i = (*pCircleQueue).head;
  51. int count = 0;
  52. do
  53. {
  54. printf("%3d", (*pCircleQueue).arr[(i++) % EN]);
  55. if (9 == ++count)
  56. break;
  57. } while ((((*pCircleQueue).tail + 1) % EN != i % EN) && (i %EN < (*pCircleQueue).tail));
  58. printf("\n");
  59. }
  60. DT circleQueueGetHead(CircleQueue * pCircleQueue)
  61. {
  62. if (NULL == pCircleQueue)
  63. abort();
  64. if (circleQueueIsEmpty(pCircleQueue))
  65. return -1;
  66. return (*pCircleQueue).arr[(*pCircleQueue).head];
  67. }
  68. void deCircleQueue(CircleQueue * pCircleQueue)
  69. {
  70. if (NULL == pCircleQueue)
  71. abort();
  72. if (circleQueueIsEmpty(pCircleQueue))
  73. return;
  74. (*pCircleQueue).head = ((*pCircleQueue).head + 1) % EN;
  75. }
  1. ///数组正向环形队列.c
  2. #include "CircleQueue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. int main01(void)
  6. {
  7. CircleQueue circleQueue;
  8. initCircleQueue(&circleQueue);
  9. for (int i = 0; i < 9; ++i)
  10. {
  11. enCircleQueue(&circleQueue, i + 1);
  12. showCircleQueue(&circleQueue);
  13. }
  14. while (!circleQueueIsEmpty(&circleQueue))
  15. {
  16. printf("数组正向环形队列出队:%3d \n", circleQueueGetHead(&circleQueue));
  17. deCircleQueue(&circleQueue);
  18. showCircleQueue(&circleQueue);
  19. }
  20. system("pause");
  21. }

程序片段(16):Queue.h+Queue.c+数组正向环形队列.c

内容概要:02.数组正向环形队列(标准版)

  1. ///Queue.h
  2. #pragma once
  3. #define DT int
  4. #define EN 10
  5. //01.采用数组模拟队列的两种特点:
  6. // 1.假设待用于模拟的数组共有N个元素
  7. // 2.两种目标队列模型:
  8. // 普通队列:数组正向队列,使用N个元素
  9. // 环形队列:数组正向环形队列,使用N-1个元素
  10. //注:环形队列,删除一个元素便无需移动
  11. typedef struct queue
  12. {
  13. DT arr[EN];
  14. int head;
  15. int tail;
  16. }Queue;
  17. void initQueue(Queue * pQueue);
  18. int queueIsFull(Queue * pQueue);
  19. void enQueue(Queue * pQueue, DT data);
  20. int queueIsEmpty(Queue * pQueue);
  21. void showQueue(Queue * pQueue);
  22. DT queueGetHead(Queue * pQueue);
  23. void deQueue(Queue * pQueue);
  1. ///Queue.c
  2. #include "Queue.h"
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. #include <stdio.h>
  6. void initQueue(Queue * pQueue)
  7. {
  8. if (NULL == pQueue)
  9. abort();
  10. memset((*pQueue).arr, 0, EN * sizeof(DT));
  11. (*pQueue).tail = (*pQueue).head = 0;
  12. }
  13. //01.区分:数组正向环形队列的两种情况
  14. // 1.空队列:起始位置=终止位置
  15. // 2.满队列:起始位置=(终止位置+1)%EN;
  16. //注:关于环形队列的面试填空问题
  17. // 1.预留一个空数组元素用作这两种情况的区分
  18. // 空队列和满队列的准确区分
  19. // 2.使得环形队列的循环利用情况得到维持
  20. // 能够循环利用到环形队列当中的每个元素位置
  21. // 3.极端情况分析:
  22. // (1).头尾:head<tail
  23. // (2).中间:head>tail
  24. // (3).相同:head=tail
  25. int queueIsFull(Queue * pQueue)
  26. {
  27. if (NULL == pQueue)
  28. abort();
  29. if ((*pQueue).head == ((*pQueue).tail + 1) % EN)
  30. {
  31. return 1;
  32. }
  33. return 0;
  34. }
  35. //02.数组正向环形队列的入队比数组正向队列麻烦多了:
  36. // 1.特点就是:始终在模拟正向环形队列的数组当中空余一个元素位置
  37. // 用作区分空队列和满队列以及维持环形队列的循环状况
  38. // 2.走环形的特点!充分利用取余运算符的特点
  39. //注:取余运算符能够杜绝两种特殊情况:
  40. // 起点刚好冲数组首位置开始的情况
  41. // 起点不是位于数组首位置的情况
  42. // 特:在这两种情况之下都能够维持空余一个元素位置的特点
  43. //最后一个位置无论何种情况都不会被使用到!
  44. void enQueue(Queue * pQueue, DT data)
  45. {
  46. if (NULL == pQueue)
  47. abort();
  48. if (queueIsFull(pQueue))
  49. return;
  50. (*pQueue).arr[(*pQueue).tail] = data;
  51. (*pQueue).tail = ((*pQueue).tail + 1) % EN;//就是为了一定要空余最后一个位置
  52. }
  53. //03.空队列的两种情况:
  54. // 重合点为:(起点位置or终点位置)
  55. // 重合点为:模拟数组的任何位置!
  56. //注:实质上就是两点重合!
  57. int queueIsEmpty(Queue * pQueue)
  58. {
  59. if (NULL == pQueue)
  60. abort();
  61. if ((*pQueue).head == (*pQueue).tail)
  62. return 1;
  63. return 0;
  64. }
  65. void showQueue(Queue * pQueue)
  66. {
  67. if (NULL == pQueue)
  68. abort();
  69. for (int i = (*pQueue).head; i % EN < (*pQueue).tail ; ++i)
  70. {
  71. printf("%3d", (*pQueue).arr[i]);
  72. }
  73. printf("\n");
  74. }
  75. DT queueGetHead(Queue * pQueue)
  76. {
  77. if (NULL == pQueue)
  78. abort();
  79. if (queueIsEmpty(pQueue))
  80. return;
  81. return (*pQueue).arr[(*pQueue).head];
  82. }
  83. void deQueue(Queue * pQueue)
  84. {
  85. if (NULL == pQueue)
  86. abort();
  87. if (queueIsEmpty(pQueue))
  88. return;
  89. (*pQueue).head = ((*pQueue).head + 1) % EN;
  90. }
  1. ///数组正向环形队列.c
  2. #include "Queue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. int main02(void)
  6. {
  7. //for (int i = 0;;++i)
  8. //{
  9. // printf("%2d", i %10);
  10. //}
  11. Queue queue;
  12. initQueue(&queue);
  13. for (int i = 0; i < EN - 1; ++i)
  14. {
  15. enQueue(&queue, i + 1);
  16. showQueue(&queue);
  17. }
  18. while (!queueIsEmpty(&queue))
  19. {
  20. printf("%3d", queueGetHead(&queue));
  21. deQueue(&queue);
  22. }
  23. system("pause");
  24. }
  25. //01.环形队列:
  26. // 1.最后一个坑用于表示模拟结束:标识结束
  27. // 标识结束+区分空队列和满队列+可循环利用
  28. // 2.环形队列原理深究:
  29. // 环形队列的优先级问题-->顺序队列同样有
  30. //注:优先队列
  31. //02.环形队列的应用:
  32. // 1.高效应用
  33. // 2.操作系统在一段时间之内只能运行一个线程
  34. //03.操作系统的特点:
  35. // 1.我一段时间限定内只能运行一段儿程序,所以操作系统
  36. // 为每一条线程分配相应的时间片,然后获取时间片之后
  37. // 就开始执行-->操作系统1秒钟有1000次夺回控制权
  38. // 2.Windows属于抢占式操作系统
  39. // 操作系统时时刻刻夺回控制权,在重新进行分配
  40. // 3.冻结状态与解冻状态的体现
  41. //04.处理队列的时候需要将数据更替为HANDLE类型
  42. //05.使用数组构建环形队列比使用链表构建环形队列简单多了
  43. //06.数组正向环形队列相比数组正向队列的好处:
  44. // 删除一个元素之后不需要进行移动,消耗效率

程序片段(17):CircleQueue.h+CircleQueue.c+数组正向环形队列.c

内容概要:01.数组正向环形队列

  1. ///CircleQueue.h
  2. #pragma once
  3. #define DT int
  4. #define EN 10
  5. //01.数组模拟队列:
  6. // 普通队列:使用N个数组元素
  7. // 环形队列:使用N-1个数组元素
  8. typedef struct circleQueue
  9. {
  10. DT arr[EN];
  11. int head;
  12. int tail;
  13. }CircleQueue;
  14. void initCircleQueue(CircleQueue * pCircleQueue);
  15. int circleQueueIsFull(CircleQueue * pCircleQueue);
  16. void enCircleQueue(CircleQueue * pCircleQueue, DT data);
  17. int circleQueueIsEmpty(CircleQueue * pCircleQueue);
  18. void showCircleQueue(CircleQueue * pCircleQueue);
  19. DT circleQueueGetHead(CircleQueue * pCircleQueue);
  20. void deCircleQueue(CircleQueue * pCircleQueue);
  1. ///CircleQueue.c
  2. #include "CircleQueue.h"
  3. #include <stdlib.h>
  4. #include <memory.h>
  5. #include <stdio.h>
  6. void initCircleQueue(CircleQueue * pCircleQueue)
  7. {
  8. if (NULL == pCircleQueue)
  9. abort();
  10. memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
  11. (*pCircleQueue).tail = (*pCircleQueue).head = 0;
  12. }
  13. int circleQueueIsFull(CircleQueue * pCircleQueue)
  14. {
  15. if (NULL == pCircleQueue)
  16. abort();
  17. if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)//满队列
  18. return 1;
  19. return 0;
  20. }
  21. void enCircleQueue(CircleQueue * pCircleQueue, DT data)
  22. {
  23. if (NULL == pCircleQueue)
  24. abort();
  25. if (circleQueueIsFull(pCircleQueue))
  26. return;
  27. (*pCircleQueue).arr[(*pCircleQueue).tail] = data;//当前填充位置
  28. (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;//下个填充位置+保证连续存储
  29. }
  30. int circleQueueIsEmpty(CircleQueue * pCircleQueue)
  31. {
  32. if (NULL == pCircleQueue)
  33. abort();
  34. if ((*pCircleQueue).head == (*pCircleQueue).tail)//空队列
  35. return 1;
  36. return 0;
  37. }
  38. void showCircleQueue(CircleQueue * pCircleQueue)
  39. {
  40. if (NULL == pCircleQueue)
  41. abort();
  42. if (circleQueueIsEmpty(pCircleQueue))
  43. return;
  44. for (int i = (*pCircleQueue).head; i%EN < (*pCircleQueue).tail; ++i)//i<=>i%EN:这里是环形队列没有出现特殊情况的特点!
  45. {//数组正向环形队列:1.不确定数组环形队列元素个数+2.不确定环形队列的起始元素和终止元素位置(因此打印无法控制)
  46. printf("%3d", (*pCircleQueue).arr[i]);
  47. }
  48. printf("\n");
  49. }
  50. DT circleQueueGetHead(CircleQueue * pCircleQueue)
  51. {
  52. if (NULL == pCircleQueue)
  53. abort();
  54. if (circleQueueIsEmpty(pCircleQueue))
  55. return -1;
  56. return (*pCircleQueue).arr[(*pCircleQueue).head];
  57. }
  58. void deCircleQueue(CircleQueue * pCircleQueue)
  59. {
  60. if (NULL == pCircleQueue)
  61. abort();
  62. if (circleQueueIsEmpty(pCircleQueue))
  63. return;
  64. (*pCircleQueue).head = ((*pCircleQueue).head + 1) % EN;
  65. }
  1. ///数组正向环形队列.c
  2. #include "CircleQueue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <Windows.h>
  6. int main01(void)
  7. {
  8. CircleQueue circleQueue = { 0 };
  9. initCircleQueue(&circleQueue);
  10. for (int i = 0; i < 9; ++i)
  11. {
  12. enCircleQueue(&circleQueue, i + 1);
  13. showCircleQueue(&circleQueue);
  14. }
  15. while (!circleQueueIsEmpty(&circleQueue))
  16. {
  17. printf("%3d \n", circleQueueGetHead(&circleQueue));
  18. deCircleQueue(&circleQueue);
  19. showCircleQueue(&circleQueue);
  20. }
  21. system("pause");
  22. }
  23. CircleQueue circleQueue = { 0 };
  24. DWORD WINAPI producer(void * p)
  25. {
  26. printf("生产者第01次执行生产任务! \n");
  27. int data = 0;
  28. while (!circleQueueIsFull(&circleQueue))
  29. {//生产者:一次性补充满库存黁量
  30. enCircleQueue(&circleQueue, ++data);
  31. printf("生产者生产了%3d! \n", data);
  32. }
  33. Sleep(1000);
  34. HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"producer");
  35. SetEvent(event1);
  36. int i = 1;
  37. while (++i)
  38. {
  39. HANDLE event2 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
  40. WaitForSingleObject(event2, INFINITE);
  41. printf("生产者第%02d次执行生产任务! \n", i);
  42. while (!circleQueueIsFull(&circleQueue))
  43. {
  44. enCircleQueue(&circleQueue, ++data);
  45. printf("生产者生产了%3d! \n", data);
  46. }
  47. Sleep(1000);
  48. SetEvent(event1);
  49. }
  50. return 0;
  51. }
  52. DWORD WINAPI consumer(void * p)
  53. {
  54. int i = 0;
  55. while (++i)
  56. {
  57. HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"producer");
  58. WaitForSingleObject(event1, INFINITE);
  59. printf("消费者第%02d次执行消费任务! \n", i);
  60. int num = 9;
  61. for (int j = 0; j < num; ++j)
  62. {
  63. if (!circleQueueIsEmpty(&circleQueue))
  64. {
  65. printf("消费者消费了%3d! \n", circleQueueGetHead(&circleQueue));
  66. deCircleQueue(&circleQueue);
  67. }
  68. }
  69. Sleep(1000);
  70. HANDLE event2 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
  71. SetEvent(event2);
  72. }
  73. return 0;
  74. }
  75. int main02(void)
  76. {
  77. HANDLE event1 = CreateEvent(NULL, FALSE, FALSE, L"producer");
  78. HANDLE event2 = CreateEvent(NULL, FALSE, FALSE, L"consumer");
  79. HANDLE threadArr[2] = { 0 };
  80. threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
  81. threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
  82. //WaitForSingleObject(producer, INFINITE);//可以直接等待单个线程任务执行结束以后!
  83. WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
  84. CloseHandle(event1);
  85. system("pause");
  86. }
  87. //01.消费者不管买走多少,都需要将存储结构塞满
  88. // 紧缺产品:针对于畅销产品的库存解决方案
  89. // 随时保持库存充足
  90. // 停滞产品:针对于停滞产品的库存解决方案
  91. // 在满足市场供需的情况之下,库存越少越好
  92. //02.Scanf不是一个线程安全的函数
  93. // 1.所以需要手动进行安全检查
  94. // 2.它也是系统出现漏洞的原因之一
  95. //03.防止进栈压栈冲突:延迟
  96. // 互锁:不要让生产者边生产而消费者边消费
  97. // 解决:生产者完成之后消费者进行消费
  98. //注:以上情况不符合现实情况,现实情况之下需要解决多线程异步并发访问冲突问题
  99. //04.生产者&消费者:
  100. // 1.环形队列的仓库,保证这个库存-->生产的是紧缺产品(随时满足库存量)
  101. // 2.库存一定需要填满(针对于畅销紧缺产品)
  102. //05.工厂设计模式:
  103. // 1.同时生产多个产品-->产品&线程开辟-->平衡调度线程
  104. // 工厂:多线程
  105. // 2.前台卖货:平衡调度
  106. // 库存控制,畅销与非畅销
  107. // 3.消费者消费:千变万化
  108. //注:区分(生产者与消费者)和(工厂)两种设计模式的区别:
  109. // 生产者与消费者:单产品
  110. // 工厂:多产品
  111. //06.链式队列(无线)&栈(有限)
  112. // 服务器几十万几百万的多线程操作
  113. //07.内存数据库:
  114. // 1.所有数据都载入内存-->发出请求
  115. // 2.文件载入内存
  116. // 3.消费者提出(需求),生产者进行(生产)
  117. // 4.线程不断的进行加载
  118. // 5.防止多线程并发访问
  119. // 6.迁移到CGI: 手机查询
  120. // 7.多线程与队列问题-->稳定与不稳定

程序片段(18):Queue.h+Queue.c+Main.c

内容概要:02.链表反向队列

  1. ///Queue.h
  2. #pragma once
  3. #define DT int
  4. typedef struct node
  5. {
  6. DT data;
  7. struct node * pNext;
  8. }Node;
  9. void initQueue(Node ** ppQueue);
  10. void enQueue(Node ** ppQueue, DT data);
  11. void showQueue(Node * pQueue);
  12. DT queueGetHead(Node * pQueue);
  13. void deQueue(Node ** ppQueue);
  1. ///Queue.c
  2. #include "Queue.h"
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. void initQueue(Node ** ppQueue)
  6. {
  7. if (NULL == ppQueue)
  8. abort();
  9. *ppQueue = NULL;
  10. }
  11. void enQueue(Node ** ppQueue, DT data)
  12. {
  13. if (NULL == ppQueue)//无队列
  14. abort();
  15. Node * pNew = (Node *)malloc(sizeof(Node));
  16. pNew->data = data;
  17. pNew->pNext = NULL;
  18. if (NULL == *ppQueue)//空队列
  19. {
  20. *ppQueue = pNew;
  21. return;
  22. }
  23. pNew->pNext = *ppQueue;
  24. *ppQueue = pNew;
  25. }
  26. void showQueue(Node * pQueue)
  27. {
  28. if (NULL == pQueue)
  29. return;
  30. for (Node * pTmp = pQueue; NULL != pTmp; pTmp = pTmp->pNext)
  31. {
  32. printf("%3d", pTmp->data);
  33. }
  34. printf("\n");
  35. }
  36. DT queueGetHead(Node * pQueue)
  37. {
  38. if (NULL == pQueue)
  39. abort();
  40. Node * pTmp = pQueue;
  41. while (NULL != pTmp->pNext)
  42. {
  43. pTmp = pTmp->pNext;
  44. }
  45. return pTmp->data;
  46. }
  47. void deQueue(Node ** ppQueue)
  48. {
  49. if (NULL == ppQueue)
  50. abort();
  51. if (NULL == *ppQueue)
  52. return;
  53. if (NULL == (*ppQueue)->pNext)
  54. {
  55. free(*ppQueue);
  56. *ppQueue = NULL;
  57. return;
  58. }
  59. Node * pTmp = *ppQueue;
  60. while (NULL != pTmp->pNext->pNext)
  61. {
  62. pTmp = pTmp->pNext;
  63. }
  64. free(pTmp->pNext);
  65. pTmp->pNext = NULL;
  66. }
  1. ///Main.c
  2. #include "Queue.h"
  3. #include <stdlib.h>
  4. #include <Windows.h>
  5. //01.链表反向队列:
  6. // 全局变量:用作跨线程通信变量
  7. Node * pQueue = NULL;
  8. //02.生产者消费者模式之生产者:
  9. // 1.时时刻刻盯着链表反向队列结构
  10. // 2.区分:畅销产品与非畅销产品
  11. //注:避免过度消耗资源的情况发生
  12. DWORD WINAPI producer(void * p)
  13. {//非畅销产品
  14. int i = 0;
  15. while (++i)
  16. {
  17. if (NULL == pQueue)
  18. {
  19. enQueue(&pQueue, i);
  20. printf("生产者生产了产品%3d! \n", i);
  21. }
  22. Sleep(1000);
  23. }
  24. return 0;
  25. }
  26. DWORD WINAPI consumer(void * p)
  27. {
  28. int i = 0;
  29. while (++i)
  30. {
  31. MessageBoxA(0, "wait", "consumer", 0);
  32. printf("消费者消费了%3d! \n", queueGetHead(pQueue));
  33. deQueue(&pQueue);
  34. }
  35. return 0;
  36. }
  37. int main01(void)
  38. {
  39. HANDLE threadArr[2] = { 0 };
  40. threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
  41. threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
  42. WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
  43. system("pause");
  44. }
  45. //01.生产者与消费者(设计模式):
  46. // 1.链表反向队列:作为流水线
  47. // 数组(正向&反向)队列&数组(正向&反向)环形队列&链表(正向&反向)队列
  48. // 2.线程结构:生产者&消费者
  49. // 3.流程原理:
  50. // (1).当流水线为空的时候,生产者生产
  51. // (2).生产者:非畅销&畅销(视具体情况而定)
  52. // (3).消费者:手动控制,可以获取任意个数
  53. // 设计模式:看不明白的主要原因是因为多线程
  54. // 单线程没有意义,多线程才有意义
  55. //02.生产者&消费者:
  56. // 1.生产"紧缺"产品&生产"非紧缺"产品
  57. // 2.生产者&消费者所做事情:
  58. // (1),生产者时时刻刻检测数据结构是否已经填充满了
  59. // 没有满需要插入数据-->链式队列:锁定数目就行了(防止无限仓库产生)
  60. // 理论上都不推荐使用链式队列:因为过渡消耗资源
  61. // -->链式栈不存在满的情况:可以进行无限拓展
  62. // (2).用于软件开发的两种情况:
  63. // 1).生产&消费分开做
  64. // 2).工厂模式更加复杂(不同类型的生产者与消费者模式)
  65. //03.理解生产者与消费者
  66. // 1.生产者需要保证至少有一个
  67. // 2.消费者的消费情况是随机消费的
  68. // 3.消费者需要配合生产着
  69. // 一个入队,一个出队[消费者的消费是个不确定的数据]
  70. // 4.线程通信中间使用最多的是什么?
  71. // 事件&互斥量&信号量

程序片段(19):Queue.h+Queue.c+01.Event通信(生产者消费者).cpp+02.Semaphore通信(生产者消费者).c

内容概要:03.生产者与消费者模式

  1. ///Queue.h
  2. #pragma once
  3. #define DT int
  4. typedef struct node
  5. {
  6. DT data;
  7. struct node * pNext;
  8. }Node;
  9. void initQueue(Node ** ppQueue);
  10. void enQueue(Node ** ppQueue, DT data);
  11. void showQueue(Node * pQueue);
  12. DT queueGetHead(Node * pQueue);
  13. void deQueue(Node ** ppQueue);
  1. ///Queue.c
  2. #include "Queue.h"
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. void initQueue(Node ** ppQueue)
  6. {
  7. if (NULL == ppQueue)
  8. abort();
  9. *ppQueue = NULL;
  10. }
  11. void enQueue(Node ** ppQueue, DT data)
  12. {
  13. if (NULL == ppQueue)//无队列
  14. abort();
  15. Node * pNew = (Node *)malloc(sizeof(Node));
  16. pNew->data = data;
  17. pNew->pNext = NULL;
  18. if (NULL == *ppQueue)//空队列
  19. {
  20. *ppQueue = pNew;
  21. return;
  22. }
  23. pNew->pNext = *ppQueue;
  24. *ppQueue = pNew;
  25. }
  26. void showQueue(Node * pQueue)
  27. {
  28. if (NULL == pQueue)
  29. return;
  30. for (Node * pTmp = pQueue; NULL != pTmp; pTmp = pTmp->pNext)
  31. {
  32. printf("%3d", pTmp->data);
  33. }
  34. printf("\n");
  35. }
  36. DT queueGetHead(Node * pQueue)
  37. {
  38. if (NULL == pQueue)
  39. abort();
  40. Node * pTmp = pQueue;
  41. while (NULL != pTmp->pNext)
  42. {
  43. pTmp = pTmp->pNext;
  44. }
  45. return pTmp->data;
  46. }
  47. void deQueue(Node ** ppQueue)
  48. {
  49. if (NULL == ppQueue)
  50. abort();
  51. if (NULL == *ppQueue)
  52. return;
  53. if (NULL == (*ppQueue)->pNext)
  54. {
  55. free(*ppQueue);
  56. *ppQueue = NULL;
  57. return;
  58. }
  59. Node * pTmp = *ppQueue;
  60. while (NULL != pTmp->pNext->pNext)
  61. {
  62. pTmp = pTmp->pNext;
  63. }
  64. free(pTmp->pNext);
  65. pTmp->pNext = NULL;
  66. }
  1. ///01.Event通信(生产者消费者).cpp
  2. #include "Queue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <Windows.h>
  6. Node * pQueue = NULL;
  7. DWORD WINAPI producer(void * p)
  8. {
  9. enQueue(&pQueue, 1);
  10. int i = 1;
  11. while (++i)
  12. {
  13. HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
  14. WaitForSingleObject(event1, INFINITE);
  15. printf("生产者生产了%3d! \n", i);
  16. enQueue(&pQueue, i);
  17. }
  18. return 0;
  19. }
  20. DWORD WINAPI consumer(void * p)
  21. {
  22. int i = 0;
  23. while (++i)
  24. {
  25. MessageBoxA(0, "wait", "wait", 0);
  26. printf("消费者消费了%3d! \n", queueGetHead(pQueue));
  27. deQueue(&pQueue);
  28. HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
  29. SetEvent(event1);
  30. }
  31. return 0;
  32. }
  33. int main01(void)
  34. {
  35. HANDLE event1 = CreateEvent(NULL, FALSE, FALSE, L"consumer");
  36. HANDLE threadArr[2] = { 0 };
  37. threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
  38. threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
  39. WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
  40. CloseHandle(event1);
  41. system("pause");
  42. return 1;
  43. }
  44. //01.消费完成之后设置事件的触发
  45. //02.每秒钟进行检测,浪费资源
  46. //03.事件的关键步骤:
  47. // CloseHandle(event);
  48. //04.在一个线程里面不需要死循环:
  49. // 因为它在这儿i不断的进行自增,增加的次数不确定
  50. //05.事件通信&信号量通信
  1. ///02.Semaphore通信(生产者消费者).c
  2. #include "Queue.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <Windows.h>
  6. Node * pQueue = NULL;
  7. DWORD WINAPI producer(void * p)
  8. {
  9. HANDLE sema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "consumer");
  10. enQueue(&pQueue, 1);
  11. int i = 1;
  12. while (++i)
  13. {
  14. WaitForSingleObject(sema, INFINITE);
  15. printf("生产者生产了%3d! \n", i);
  16. enQueue(&pQueue, i);
  17. }
  18. return 0;
  19. }
  20. DWORD WINAPI consumer(void * p)
  21. {
  22. HANDLE sema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "consumer");
  23. int i = 0;
  24. while (++i)
  25. {
  26. MessageBoxA(0, "wait", "consumer", 0);
  27. printf("消费者消费了%3d! \n", queueGetHead(pQueue));
  28. deQueue(&pQueue);
  29. ReleaseSemaphore(sema, 1, NULL);
  30. }
  31. return 0;
  32. }
  33. int main02(void)
  34. {
  35. HANDLE sema = CreateSemaphoreA(NULL, 0, 1, "consumer");
  36. HANDLE threadArr[2] = { 0 };
  37. threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
  38. threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
  39. WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
  40. CloseHandle(sema);
  41. system("pause");
  42. }
  43. //01.信号量解决生产者与消费者问题:
  44. // C++称之为工厂设计模式
  45. //02.事件-->互斥量解决线程通信问题:
  46. // 事件-->信号量问题分析
  47. //03.设计模式结合多线程比较好理解
  48. // 两个变量之间的双方通信规则

20160227.CCPP体系详解(0037天)的更多相关文章

  1. 20160129.CCPP体系详解(0008天)

    程序片段(01):函数.c+call.c+测试.cpp 内容概要:函数 ///函数.c #include <stdio.h> #include <stdlib.h> //01. ...

  2. 20160226.CCPP体系详解(0036天)

    程序片段(01):01.多线程.c+02.多线程操作.c 内容概要:多线程 ///01.多线程.c #include <stdio.h> #include <stdlib.h> ...

  3. 20160208.CCPP体系详解(0018天)

    程序片段(01):main.c 内容概要:PointWithOutInit #include <stdio.h> #include <stdlib.h> //01.野指针详解: ...

  4. 20160206.CCPP体系详解(0016天)

    代码片段(01):.指针.c+02.间接赋值.c 内容概要:内存 ///01.指针 #include <stdio.h> #include <stdlib.h> //01.取地 ...

  5. 20160205.CCPP体系详解(0015天)

    程序片段(01):01.杨辉三角.c 内容概要:杨辉三角 #include <stdio.h> #include <stdlib.h> #define N 10 //01.杨辉 ...

  6. 20160204.CCPP体系详解(0014天)

    程序片段(01):define.h+data.h&data.c+control.h&control.c+view.h&view.c+AI.h&AI.c+main.c 内 ...

  7. 20160203.CCPP体系详解(0013天)

    程序片段(01):数组.c+02.数组初始化语法.c 内容概要:数组 ///01.数组.c #include <stdio.h> #include <stdlib.h> //0 ...

  8. 20160128.CCPP体系详解(0007天)

    以下内容有所摘取,进行了某些整理和补充 论浮点数的存储原理:float浮点数与double浮点数的二进制存储原理–>阶码 浮点数转二进制 1.整数int类型和浮点数float类型都是占用4个字节 ...

  9. 20160127.CCPP体系详解(0006天)

    程序片段(01):msg.c 内容概要:线程概念 #include <stdio.h> #include <stdlib.h> #include <Windows.h&g ...

随机推荐

  1. 框架学习之Spring(一IOC)----HelloWrod

    一.概述 Spring是一个开源框架,它的核心是控制反转(IOC)和面向切面(AOP).简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架. EE 开发分 ...

  2. POJ-3169 Layout---差分约束系统+Bellman

    题目链接: https://vjudge.net/problem/POJ-3169 题目大意: 一些母牛按序号排成一条直线.有两种要求,A和B距离不得超过X,还有一种是C和D距离不得少于Y,问可能的最 ...

  3. JSON定义

    如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如xml,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过 ...

  4. github git 在GitHub上创建项目并将本地项目push到网站上

    众所周知,git是与svn类似的版本控制系统,git的去中心化.分布式等的优点,在不久将来用户量大有可能超过svn, 常见的代码托管网站有GitHub,coding.net, gitee.com 码云 ...

  5. c++简单线程池实现

    线程池,简单来说就是有一堆已经创建好的线程(最大数目一定),初始时他们都处于空闲状态,当有新的任务进来,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他 ...

  6. 列表(list)之三 -如何较为均匀的将任意字符串按指定组数分组,方差最少

    当字符串的长度不是份数的整数倍时如何均匀地分割,例如:长度为11的字符串要分割成4份,有很多种分法,比如3, 3, 3, 2(前3个字符一份,中间3个一份,再中间3个一份,最后2个一份)这种是比较均匀 ...

  7. Python中if __name__ == "__main__": 的理解

    1.在很多python脚本中在最后的部分会执行一个判断语句if __name__ == "__main__:",之后还可能会有一些执行语句.那添加这个判断的目的何在? 在pytho ...

  8. Spring Boot 之Hello Word

    Spring Boot官网:http://projects.spring.io/spring-boot/ 环境准备:maven 3.3.5.jdk8.Idea 1.创建maven项目工程 2.引入st ...

  9. “百度杯”CTF比赛 九月场_123(文件备份,爆破,上传)

    题目在i春秋ctf训练营 翻看源码,发现提示: 打开user.php,页面一片空白,参考大佬的博客才知道可能会存在user.php.bak的备份文件,下载该文件可以得到用户名列表 拿去burp爆破: ...

  10. day 1——字典树练习

    cojs 173. 词链 ★☆   输入文件:link.in   输出文件:link.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述]给定一个仅包含小写字母的英文单词表, ...