目录
第1关:生产者消费者问题实践
任务描述
生产者—消费者之间设置一个具有n个缓存区的缓冲池,生
pthread_t th //th是要等待结束的线程的标识 void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。 调用实例:pthread_join(thrd1, NULL);
产者进程将他所生产的产品放入一个缓冲区;消费者进程可以从一个缓冲区中取走产品去消费。不允许消费者进程到一个空缓冲去取产品。不允许生产者进程向一个已装满产品且尚未取走的缓冲区投放产品。
使用多线程实现生产者和消费者问题模型,使用锁和信号量控制线程之间的同步。
相关知识
为了完成本关任务,你需要掌握:1.多线程相关的系统调用,2.使用锁控制进程互斥,3.使用信号量控制进程同步
多线程相关的系统调用
include <pthread.h>
创建线程 pthread_create pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument);
线程创建函数包含四个变量,分别为:
1.一个线程变量名,被创建线程的标识
2. 线程的属性指针,缺省为NULL即可
3. 被创建线程的程序代码
4. 程序代码的参数
线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:
pthread_t th //th是要等待结束的线程的标识 void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。 调用实例:pthread_join(thrd1, NULL);
使用锁控制进程互斥
在主线程中初始化锁为解锁状态
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
访问对象时的加锁操作与解锁操作
加锁 pthread_mutex_lock(&mutex)
释放锁 pthread_mutex_unlock(&mutex)
利用信号量实现进程同步
先引入头文件 #include <semaphore.h>
初始化信号量:
int sem_init(sem_t \*sem, int pshared, unsigned int value);
成功返回0,失败返回-1
参数
sem:指向信号量结构的一个指针
pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享 value:信号量的初始值
信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem);
成功返回0,失败返回-1
参数 sem:指向信号量的一个指针
信号量加1操作 int sem_post(sem_t *sem);
参数与返回同上 销毁信号量 int sem_destroy(sem_t *sem);
参数与返回同上
编程要求
请参考生产者线程的函数代码实现消费者线程的函数代码,将Consumer()函数补充完整。
测试说明
需要打印出生产者和消费者线程并发执行的情况,并满足题目所示的限制条件 预期输出结果类似下面这样。因为调度次序随机,执行结果可能和这个不同,但需要满足题目限制要求。
Produce one message:1 Consume one message:1 Produce one message:2 Consume one message:2 Produce one message:3 Consume one message:3 Produce one message:4 Consume one message:4 Produce one message:5 Consume one message:5 Produce one message:6 Produce one message:7 Consume one message:6 Consume one message:7 Produce one message:8 Produce one message:9 Consume one message:8 Produce one message:10 Consume one message:9 Consume one message:10
答案:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define SIZE 10 int in = 0; int out = 0; int buffer[SIZE]; sem_t empty; sem_t full; pthread_mutex_t mutex; void *Producer() { int nextp = 0; int i = 0; for(; i < 10; ++i) { int time = rand() % 10 + 1; usleep(time*100000); sem_wait(&empty); pthread_mutex_lock(&mutex); nextp = nextp + 1; buffer[in] = nextp; printf("Produce one message:%d\n", nextp); fflush(stdout);//printf后请一定调用这句刷新输出缓存 in = (in + 1) % SIZE; pthread_mutex_unlock(&mutex); //互斥锁解锁 sem_post(&full); } } void *Consumer() { //请补充消费者线程函数代码 int nextc=0; int i=0; for (;i<10;++i){ sem_wait(&full); //等待缓冲区非空 pthread_mutex_lock(&mutex); //加锁 nextc = buffer[out]; printf("Consume one message:%d\n",nextc); fflush(stdout); out=(out+1)%SIZE; pthread_mutex_unlock(&mutex); //解锁 sem_post(&empty); //增加一个空缓冲区 } } int main() { sem_init(&empty, 0, 10); //信号量初始化(最多容纳10条消息,容纳了10条生产者将不会生产消息) sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL); //互斥锁初始化 pthread_t producid; pthread_t consumid; pthread_create(&producid, NULL, Producer, NULL); //创建生产者线程 pthread_create(&consumid, NULL, Consumer, NULL); //创建消费者线程 pthread_join(producid, NULL); pthread_join(consumid, NULL); sem_destroy(&empty); //信号量的销毁 sem_destroy(&full); pthread_mutex_destroy(&mutex); //互斥锁的销毁 return 0; }
第2关:进程互斥和同步
任务描述
桌上有个能盛的下五个水果的空盘子。爸爸不停的向盘中放苹果或桔子,儿子不停的从盘中取出桔子享用,女儿不停的从盘中取出苹果享用。规定三人不能同时从盘中取放水果。试用信号量实现爸爸、儿子和女儿这三个进程之间的同步
相关知识
为了完成本关任务,你需要掌握:1.多线程相关的系统调用,2.使用锁控制进程互斥,3.使用信号量控制进程同步
多线程相关的系统调用
include <pthread.h>
创建线程 pthread_create pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument); 线程创建函数包含四个变量,分别为:
1.一个线程变量名,被创建线程的标识
2. 线程的属性指针,缺省为NULL即可
3. 被创建线程的程序代码
4. 程序代码的参数
线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:
pthread_t th //th是要等待结束的线程的标识 void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。 调用实例:pthread_join(thrd1, NULL);
#### 使用锁控制进程互斥 在主线程中初始化锁为解锁状态 pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); 访问对象时的加锁操作与解锁操作 加锁 pthread_mutex_lock(&mutex) 释放锁 pthread_mutex_unlock(&mutex) #### 利用信号量实现进程同步 先引入头文件#include <semaphore.h> 初始化信号量: int sem_init(sem_t *sem, int pshared, unsigned int value); 成功返回0,失败返回-1 参数 sem:指向信号量结构的一个指针 pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享 value:信号量的初始值 信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem); 成功返回0,失败返回-1 参数 sem:指向信号量的一个指针 信号量加1操作 int sem_post(sem_t *sem); 参数与返回同上 销毁信号量 int sem_destroy(sem_t *sem); 参数与返回同上 ### 编程要求 请参考爸爸线程的函数代码实现儿子和女儿线程的函数代码,将Son()函数和Daughter()函数补充完整。 ### 测试说明 需要打印出爸爸、儿子和女儿线程并发执行的情况,并满足题目所示的限制条件。 爸爸、儿子和女儿的程序流程如下: semaphore empty = 5,orange = 0,apple = 0, mutex = 1; Dad() { while(1) { wait(empty); wait(mutex); 将水果放入盘中; signal(mutex); if(放入的是桔子) signal(orange); else signal(apple); } } Son() { while(1) { wait(orange); wait(mutex); 从盘中取出一个桔子; signal(mutex); signal(empty); 享用桔子; } } Daughter() { while(1) { wait(apple); wait(mutex); 从盘中取出一个苹果; signal(mutex); signal(empty); 享用苹果; } } 输出结果参考如下: 爸爸放入了一个苹果 女儿取了一个苹果 爸爸放入了一个桔子 儿子取了一个桔子 爸爸放入了一个苹果 女儿取了一个苹果 爸爸放入了一个桔子 儿子取了一个桔子 爸爸放入了一个苹果 女儿取了一个苹果 爸爸放入了一个桔子 儿子取了一个桔子 爸爸放入了一个苹果 女儿取了一个苹果 爸爸放入了一个桔子 儿子取了一个桔子 爸爸放入了一个苹果 女儿取了一个苹果 爸爸放入了一个桔子 儿子取了一个桔子 --- 开始你的任务吧,祝你成功!
答案:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <pthread.h> sem_t apple; sem_t orange; sem_t empty; pthread_mutex_t mutex; void *Dad() { int nextp = 0; int i = 0; for(i = 0; i < 10; ++i) { int time = rand() % 10 + 1; //随机使程序睡眠0点几秒 usleep(time*100000); sem_wait(&empty); pthread_mutex_lock(&mutex); if(nextp == 0) { printf("爸爸放入了一个苹果\n"); } else { printf("爸爸放入了一个桔子\n"); } fflush(stdout); pthread_mutex_unlock(&mutex); //互斥锁解锁 if(nextp == 0) { sem_post(&apple); } else { sem_post(&orange); } nextp = 1 - nextp; } } void *Daughter() { while(1) { int time = rand() % 10 + 1; //随机使程序睡眠0点几秒 usleep(time * 100000); sem_wait(&apple); pthread_mutex_lock(&mutex); printf("女儿取了一个苹果\n") ; fflush(stdout); pthread_mutex_unlock(&mutex); //互斥锁解锁 sem_post(&empty); } } void *Son() { //请添加儿子线程的函数代码 while (1) { int time=rand()%10+1; //随机使程序睡眠0点几秒 usleep(time*100000); sem_wait(&orange); pthread_mutex_lock(&mutex); printf("儿子取了一个桔子\n"); fflush(stdout); pthread_mutex_unlock(&mutex); //互斥锁解锁 sem_post(&empty); } } int main() { sem_init(&empty, 0, 5); //信号量初始化 sem_init(&orange, 0, 0); sem_init(&apple, 0, 0); pthread_mutex_init(&mutex, NULL); //互斥锁初始化 pthread_t dadid; pthread_t daughterid; pthread_t sonid; pthread_create(&dadid, NULL, Dad, NULL); //创建爸爸线程 pthread_create(&daughterid, NULL, Daughter, NULL); //创建女儿线程 pthread_create(&sonid, NULL, Son, NULL); //创建儿子线程 pthread_join(daughterid, NULL); pthread_join(sonid, NULL); sem_destroy(&empty); //信号量的销毁 sem_destroy(&apple); sem_destroy(&orange); pthread_mutex_destroy(&mutex); //互斥锁的销毁 return 0; }