利用复习这段时间正好扩充一下博客,准备在接下来好好总结一下操作系统(软件工程专业)这门课必考的5类算法,分别是生产者消费者问题 、死锁预防算法Banker 、Page Fault的处理换页算法 、进程调度算法 、I/O访问和磁盘调度算法
生产者消费者问题
生产者消费者模型一直是同步问题中最经典的模型之一。
问题描述
消费者,生产者同时对一个缓冲区变量 进行操作。每次生产者生产 一个,每次消费者消费 一个。当缓冲区为空 时消费者不能再消费,当缓冲区满了 生产者不能再生产。
思路
对于生产者和消费者的互斥操作来说,可以用一个条件变量解决。对于为空时消费者不能消费的条件使用一个信号量标记控制是否为空,且每次生产者进行生产时通知消费者进行消费,不能生产便阻塞。同样,对于缓冲区满了生产者不能再生产,也用一个条件变量控制,思路相似,此处不在赘述。
每次生产者流程: 每次消费者流程:
实现Demo
生产者消费者多个线程实现
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 #include <iostream> #include <pthread.h> #include <unistd.h> #define MAX_BUFFER_NUM 10 #define PRODUCER_FLAG 2333 #define CUSTOMER_FLAG -1 #define INIT_FLAG 0 #define PRODUCTER_NUM 10 #define CUSTOMER_NUM 1 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t full = PTHREAD_COND_INITIALIZER;pthread_cond_t empty = PTHREAD_COND_INITIALIZER;int * buffer = new int [MAX_BUFFER_NUM];int producerIndex = 0 ;int customerIndex = 0 ;int count = 0 ;void * producer (void * pid) { int producerId = *((int *) pid); while (true ) { pthread_mutex_lock(&mutex); while (count) { pthread_cond_wait(&full, &mutex); } buffer[producerIndex] = PRODUCER_FLAG; count++; std ::cout << "生产者" << producerId << ",在" << producerIndex << "位置生产了一个," << "当前产品有" << count << "个。" << std ::endl ; producerIndex = (producerIndex + 1 ) % MAX_BUFFER_NUM; sleep(0.5 ); pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); } } void * customer (void * cid) { int customerId = *((int * ) cid); while (true ) { pthread_mutex_lock(&mutex); while (!count) { pthread_cond_wait(&empty, &mutex); } buffer[customerIndex] = CUSTOMER_FLAG; count--; std ::cout << "消费者" << customerId << ",在" << customerIndex << "位置消费一个," << "当前产品有" << count << "个。" << std ::endl ; customerIndex = (customerIndex + 1 ) % MAX_BUFFER_NUM; sleep(0.5 ); pthread_cond_signal(&full); pthread_mutex_unlock(&mutex); } } int main (int argc, const char * argv[]) { for (int i = 0 ; i < MAX_BUFFER_NUM; i++) { buffer[i] = INIT_FLAG; } pthread_t producerThreads[PRODUCTER_NUM]; pthread_t customerThreads[CUSTOMER_NUM]; for (int i = 0 ; i < PRODUCTER_NUM; i++) { pthread_create(&producerThreads[i], NULL , producer, (void *) (&i)); } for (int i = 0 ; i < PRODUCTER_NUM; i++) { pthread_create(&customerThreads[i], NULL , customer, (void *) (&i)); } for (int i = 0 ; i < PRODUCTER_NUM; i++) { pthread_join(producerThreads[i], NULL ); } for (int i = 0 ; i < CUSTOMER_NUM; i++) { pthread_join(customerThreads[i], NULL ); } return 0 ; }
输出: 从输出来看,实现了生产者消费者的同步互斥问题。
一些补充
在demo实现中我们可以看出,对于生产者和消费者的临界区处理我是先加锁再pthread_cond_wait,而思路中我们计划的是先wait再加锁。这里的原因如下,思路中的wait计划使用的是类似原语的操作,即具有原子性;而pthread库提供的wait接口int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);的实现过程为:
先将mutex解开 再操作cond 再把mutex锁回去
因此,想要让这一操作也是原子性的,我们需要把临界区的范围扩大,即提前加锁。当然了,signal与unlock的操作与思路中提到的顺序相反同样是这个原因。
参考
CSDN-liushall-【操作系统】生产者消费者问题