在今年面试的时候,被问到了关于线程和进程的问题,结果当时还没学到这一块,所以理解很不透彻,最后只能和面试官承认自己这一块没咋好好了解。最近正好操作系统把生产者消费者问题讲完了,所以今天就来记录一下我写的生产者消费者问题的C语言版本。

    生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <unistd.h>
    4. #include <pthread.h>
    5. #include <semaphore.h>
    6. #define true 1
    7. #define producerNum 3 // 有3个生产者
    8. #define consumerNum 5 // 有5个消费者
    9. #define sleepTime 1 // 等待时间为1000ms
    10. #define N 3 // 缓冲区容量
    11. int buffer[N] = {0}; // 缓冲区
    12. int in = 0; // 入队点
    13. int out = 0; // 出队点
    14. int proCount = 0; // 产品计数器
    15. int value;
    16. sem_t mutex, // 缓冲区是否被占用的标志
    17. empty, // 缓冲区剩余可放产品数量
    18. full, // 缓冲区目前存在产品数量
    19. proCmutex; // 是否同时进行生产的标志
    20. // 生产者实例
    21. void * producer(void * a){
    22. while (true) {
    23. sem_wait(&proCmutex); // 如果正在进行其他生产则一直等待
    24. proCount++; // 生产一个产品后产品数加1
    25. sem_post(&proCmutex); // 结束生产
    26. sem_wait(&empty); // 如果缓冲区已满,等待消费者消费产品
    27. sem_getvalue(&empty, &value);
    28. if (value < 0)
    29. printf("empty有问题, 为%d!\n", value);
    30. sem_wait(&mutex);
    31. buffer[in] = proCount; // 将产品存入缓冲区
    32. in = (in + 1) % N; // 指向下一个存放产品的缓冲位置
    33. printf("生产一个产品, ID为%d, 缓冲区位置为%d\n", proCount, in);
    34. sem_post(&mutex); // 释放缓冲区
    35. sem_post(&full);
    36. sem_getvalue(&empty, &value);
    37. if (value < 0)
    38. printf("empty有问题, 为%d!\n", value);
    39. sleep(sleepTime); // 等待1s
    40. }
    41. }
    42. // 消费者实例
    43. void * consumer(void *b) {
    44. while(true) {
    45. sem_wait(&full); // 缓冲区是否为空,如果为空,等待生产者生产
    46. sem_getvalue(&full, &value);
    47. if (value < 0) {
    48. printf("full有问题, 为%d!\n", value);
    49. }
    50. sem_wait(&mutex); // 如果缓冲区被占用,则一直等待
    51. int nextc = buffer[out]; // 取出需要消费的产品
    52. buffer[out] = 0; //消费完将缓冲区设置为0
    53. out = (out + 1) % N; // 移向下一次消费的产品在缓冲区中的位置
    54. printf("消费一个产品, ID为%d, 缓冲区位置为%d\n", nextc, out);
    55. sem_post(&mutex); // 释放缓冲区
    56. sem_getvalue(&empty, &value);
    57. if (value < 0)
    58. printf("empty有问题, 为%d!\n", value);
    59. sem_post(&empty);
    60. sleep(sleepTime); // 等待1s
    61. }
    62. }
    63. int main()
    64. {
    65. // 创建一个线程池,存放所有生产者和消费者实例所需要的占用的线程
    66. pthread_t threadPool[producerNum + consumerNum];
    67. int i;
    68. if (sem_init(&mutex, 0, 1) == -1)
    69. exit(1);
    70. if (sem_init(&empty, 0, N) == -1)
    71. exit(1);
    72. if (sem_init(&full, 0, 0) == -1)
    73. exit(1);
    74. if (sem_init(&proCmutex, 0, 1) == -1)
    75. exit(1);
    76. for (i = 0; i < producerNum; i++) { // 创建生产者线程放入线程池
    77. pthread_t temp; // 创建生产者线程
    78. if (pthread_create(&temp, NULL, producer, NULL) == -1) {
    79. printf("错误, 创建生产者%d号失败\n", i);
    80. exit(1);
    81. }
    82. threadPool[i] = temp; // 在线程池中逐个存放生产者线程
    83. }
    84. for (i = 0; i < consumerNum; i++) { // 创建消费者线程放入线程池
    85. pthread_t temp; // 创建消费者线程
    86. if (pthread_create(&temp, NULL, consumer, NULL) == -1) {
    87. printf("错误, 创建消费者%d号失败\n", i);
    88. exit(1);
    89. }
    90. threadPool[i+producerNum] = temp; // 在线程池中逐个存放消费者线程
    91. }
    92. void * result; // 运行线程池
    93. for (i = 0; i < producerNum + consumerNum; i++) {
    94. if (pthread_join(threadPool[i], &result) == -1) {
    95. printf("fail to recollect\n"); // 逐个开启线程
    96. exit(1);
    97. }
    98. }
    99. return 0;
    100. }

    网上关于Java和Python的版本已经有很多了,但是关于C语言的并不是特别多。其中碰到了一些很奇怪的问题,就是在Windows环境下运行时(做出一些修改),会出现打印顺序完全错乱的情况
    1.png
    而在Linux环境下运行则非常正确,运行时很有序。老师在指导我的时候认为是因为我在代码中采用的int变量导致出现的问题,因为int变量在编译为汇编语言之后,会出现汇编语言的语句较多,容易在运行中途被打断的情况,所以会导致这样的情况出现。
    如下为在Linux上运行的结果
    2.png3.png

    [

    ](https://blog.csdn.net/liushall/article/details/81569609)