用多线程同步方法解决生产者-消费者问题
- 格式:doc
- 大小:128.50 KB
- 文档页数:11
实验报告二实验名称:一、生产者-消费者问题的多线程解决方案二、设计一个执行矩阵乘法的多线程程序日期:2015-10-22 班级:13级计科学号:姓名:一、实验目的1.掌握线程的同步与互斥2.掌握生产者消费者的实现问题3.掌握多线程的编程方法4.掌握矩阵乘法的基本计算原理以及实现二、实验内容1.生产者-消费者问题的多线程解决方案2.设计一个执行矩阵乘法的多线程程序三、项目要求与分析1.请查阅资料,掌握线程创建的相关知识以及矩阵乘法的相关知识,了解java语言程序编写的相关知识2.理解线程的实验步骤在本次试验中,以“生产者-消费者”模型为依据,提供了一个多线程的“生产者-消费者”实例,编写java代码调试运行结果,得出相应的结论。
理解矩阵乘法的实验步骤四、具体实现1.生产者-消费者实例(1)创建一个缓冲信息发送接收通道接口,并创建邮箱盒子类实现,主要代码如下://通道接口public interface Channelpublic abstract void send(Object item);public abstract Object receive();}//实现接口public class MessageQueue implements Channel{private Vector queue;public MessageQueue(){queue=new Vector();}public void send(Object item){queue.addElement(ite m);}public Object receive(){if(queue.size()==0)return null;elsereturn queue.remove(0);}}(2)创建一个工厂多线程类(启动生产者和消费者),并且添加main函数进行测试,主要代码如下://工厂类与主方法public class Factory{public Factory(){Channel mailBox=new MessageQueue();Thread producerThread=new Thread(newProducer(mailBox));Thread consumerThread=new Thread(newConsumer(mailBox));producerThread.start();consumerThread.start();}public static void main(String[] args)Factory server=new Factory();}(3)创建一个线程睡眠类,用于测试,主要代码如下:public class SleepUtilities{public static void nap(){nap(NAP_TIME);}public static void nap(int duration){int sleeptime = (int)(NAP_TIME * Math.random());try{ Thread.sleep(sleeptime*1000); }catch (InterruptedException e) {}}private static final int NAP_TIME = 5;(4)创建生产者类实现Runnable,主要代码如下:public class Producer implements Runnable{private Channel mbox;public Producer(Channel mbox){this.mbox=mbox;}public void run(){Date message;while(true){SleepUtilities.nap();message=new Date();System.out.println("Producer produced "+message);mbox.send(message);}}}(5)创建消费者类实现Runnable,主要代码如下:public class Consumer implements Runnable{private Channel mbox;public Consumer(Channel mbox){this.mbox=mbox;}public void run(){Date message;while(true){SleepUtilities.nap();message=(Date)mbox.receive();if(message!=null)System.out.println("Consumer consumed "+message);}}}(6)调试程序,运行结果:2.矩阵乘法实例(1)初始化矩阵(便于观察,这里使用随机数生成矩阵),主要初始化代码如下matrix1 = new int[m][k];matrix2 = new int[k][n];matrix3 = new int[m][n];//随机初始化矩阵a,bfillRandom(matrix1);fillRandom(matrix2);static void fillRandom(int[][] x){for (int i=0; i<x.length; i++){for(int j=0; j<x[i].length; j++){//每个元素设置为0到99的随机自然数x[i][j] = (int) (Math.random() * 100);}}}(2)打印输出矩阵函数,主要代码如下:static void printMatrix(int[][] x){for (int i=0; i<x.length; i++){for(int j=0; j<x[i].length; j++){System.out.print(x[i][j]+" ");}System.out.println("");}System.out.println("");}(3)创建多线程类,并实现Runnable接口同步对矩阵进行分行计算,主要代码如下://创建线程,数量 <= 4for(int i=0; i<4; i++){if(index < m){Thread t = new Thread(new MyThread());t.start();}else{break;}synchronized static int getTask(){if(index < m){return index++;}return -1;}}class MyThread implements Runnable{int task;//@Overridepublic void run(){MultiThreadMatrix.threadCount++;while( (task = MultiThreadMatrix.getTask()) != -1 ) {System.out.println("进程:"+Thread.currentThread().getName()+"\t开始计算第"+(task+1)+"行");for(int i=0; i<MultiThreadMatrix.n; i++) {for(int j=0; j<MultiThreadMatrix.k; j++) {MultiThreadMatrix.matrix3[task][i] +=MultiThreadMatrix.matrix1[task][j] *MultiThreadMatrix.matrix2[j][i];}}}MultiThreadMatrix.threadCount--;}(4)通过不断改变矩阵大小,线程数目,,调试程序,运行结果:五、所遇问题与解决方法1.在生产者-消费者多线程试验中,刚开始没有考虑到使用线程睡眠,运行结果速度之快,没法观看数据变化,后面定义了睡眠控制,使得问题得以解决2.在多线程矩阵开发实验中,刚开始定义矩阵太小,测试结果不太明显,后面通过把矩阵改大,并且线程数目不断变化使得结果明显。
操作系统中的经典问题——⽣产者消费者问题(两种⽅式实现)操作系统中的经典问题——⽣产者消费者问题(两种⽅式实现)1、问题引⼊:什么是⽣产者消费者问题?⽣产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是⼀个多线程同步问题的经典案例。
该问题描述了共享固定⼤⼩缓冲区的两个线程——即所谓的“⽣产者”和“消费者”——在实际运⾏时会发⽣的问题。
⽣产者的主要作⽤是⽣成⼀定量的数据放到缓冲区中,然后重复此过程。
与此同时,消费者也在缓冲区消耗这些数据。
该问题的关键就是要保证⽣产者不会在缓冲区满时加⼊数据,消费者也不会在缓冲区中空时消耗数据。
.要解决该问题,就必须让⽣产者在缓冲区满时休眠(要么⼲脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,⽣产者才能被唤醒,开始往缓冲区添加数据。
同样,也可以让消费者在缓冲区空时进⼊休眠,等到⽣产者往缓冲区添加数据之后,再唤醒消费者。
通常采⽤进程间通信的⽅法解决该问题。
如果解决⽅法不够完善,则容易出现死锁的情况。
出现死锁时,两个线程都会陷⼊休眠,等待对⽅唤醒⾃⼰。
该问题也能被推⼴到多个⽣产者和消费者的情形。
2、问题分析该问题需要注意的⼏点:1. 在缓冲区为空时,消费者不能再进⾏消费2. 在缓冲区为满时,⽣产者不能再进⾏⽣产3. 在⼀个线程进⾏⽣产或消费时,其余线程不能再进⾏⽣产或消费等操作,即保持线程间的同步4. 注意条件变量与互斥锁的顺序由于前两点原因,因此需要保持线程间的同步,即⼀个线程消费(或⽣产)完,其他线程才能进⾏竞争CPU,获得消费(或⽣产)的机会。
对于这⼀点,可以使⽤条件变量进⾏线程间的同步:⽣产者线程在product之前,需要wait直⾄获取⾃⼰所需的信号量之后,才会进⾏product的操作;同样,对于消费者线程,在consume之前需要wait直到没有线程在访问共享区(缓冲区),再进⾏consume的操作,之后再解锁并唤醒其他可⽤阻塞线程。
C语⾔之⽣产者与消费者模型多线程并发应⽤程序有⼀个经典的模型,即⽣产者/消费者模型。
系统中,产⽣消息的是⽣产者,处理消息的是消费者,消费者和⽣产者通过⼀个缓冲区进⾏消息传递。
⽣产者产⽣消息后提交到缓冲区,然后通知消费者可以从中取出消息进⾏处理。
消费者处理完信息后,通知⽣产者可以继续提供消息。
要实现这个模型,关键在于消费者和⽣产者这两个线程进⾏同步。
也就是说:只有缓冲区中有消息时,消费者才能够提取消息;只有消息已被处理,⽣产者才能产⽣消息提交到缓冲区。
我们⽤⼀个队列来做这个缓冲区,产⽣的消息我们放到这个队列中去,如果这个队列满了,则不放⼊消息,我们这个队列⼤⼩是10,能够存放10条消息。
然后消费者去消费,消费者去这个缓冲区⾥去取数据,同样,如果缓冲区⾥没有数据,那么就不会消费。
这⼀模型的核⼼就是消费者和⽣产者⼀起去抢互斥锁,谁抢到了这个锁谁就有资格对这个缓冲区进⾏相关操作。
分析 ⼀线程负责⽣产数据,另⼀部分线程负责消费数据。
问题1:如果产⽣的块,消费的慢,⽣产者容易饿死 问题2:如果⽣产的慢,消费的快,消费者容易饿死 只有把两个问题协调好,才能最⼤限度的提⾼效率。
代码:#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#define MAX 20char storage[MAX] = {};int count = 0;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t full = PTHREAD_COND_INITIALIZER;pthread_cond_t empty = PTHREAD_COND_INITIALIZER;void show_storage(char* role,char* op,char prod){printf("%s:",role);for(int i=0; i<count; i++){printf("%c",storage[i]);}printf("%s%c\n",op,prod);}void* pro_run(void* arg){char* who = "⽣产者";while(1){pthread_mutex_lock(&mutex);while(count >= MAX){printf("%s:满仓\n",who);pthread_cond_wait(&full,&mutex);}char prod = 'A'+rand()%26;storage[count++] = prod;show_storage(who,"->",prod);pthread_cond_signal(&empty);pthread_mutex_unlock(&mutex);usleep(rand()%100*1000);}}void* con_run(void* arg){char* who = "消费者";while(1){pthread_mutex_lock(&mutex);while(count <= 0){printf("%s:空仓\n",who);pthread_cond_wait(&empty,&mutex);}char prod = storage[count--];show_storage(who,"<-",prod);pthread_cond_signal(&full);pthread_mutex_unlock(&mutex);usleep(rand()%100*1000);}}int main(){pthread_t tid1,tid2;pthread_create(&tid1,NULL,pro_run,NULL); pthread_create(&tid2,NULL,con_run,NULL); getchar();}实现:。
多线程常考的算法题
多线程算法题常常考察对并行处理和线程同步的理解。
以下是一些常考的多线程算法题:
1.筷子原理(或称二叉堆栈):有两个栈,它们共享一段连续的内存。
这两个栈的指针分别为high 和low,每次操作都需要两个栈的指针进行比较,然后移动指针。
请设计一个线程安全的筷子算法。
2.生产者消费者问题:生产者生产物品放入缓冲区,消费者从缓冲区取出物品。
当缓冲区满时,生产者需要等待消费者取出物品;当缓冲区为空时,消费者需要等待生产者放入物品。
如何用多线程解决此问题?
3.栅栏问题:有一串数列,你可以把它想象成一排栅栏,每次只能向右移动一位,求最少多少次可以移动到最右边。
如何用多线程加速求解?
4.银行家算法:银行家算法是一种避免死锁的资源调度算法。
假设有多个进程请求资源,如何用多线程实现银行家算法?
5.线程池问题:如何设计一个线程池,使得任务队列不会溢出,并且能够充分利用多核资源?
6.锁的粒度问题:如何选择合适的锁粒度来平衡线程安全和性能?
7.读写锁问题:如何使用读写锁实现一个线程安全的缓存系统?
8.死锁检测与恢复:如何检测一个多线程系统中的死锁,并在检测到死锁后如何恢复?
9.线程同步问题:如何使用信号量、互斥锁等工具实现线程同步?
10.分布式系统中的一致性问题:如何在分布式系统中实现强一致性?
以上是一些常见的多线程算法题,通过解决这些问题,可以加深对多线程并行处理和线程同步的理解。
多线程项目经典案例
1. 生产者和消费者模型:一个经典的多线程项目案例是生产者和消费者模型。
该项目中有两类线程,分别是生产者线程和消费者线程。
生产者线程负责生成产品,并将产品放入一个共享的缓冲区中,而消费者线程则负责从缓冲区中取出产品进行消费。
2. 银行取款模型:在这个多线程项目中,会有多个用户同时通过ATM机进行取款操作。
每个用户都是一个线程,通过并行执行的方式进行取款操作。
为了避免多个用户同时从某一账户中取款而导致数据不一致的问题,需要使用同步机制来保护共享数据。
3. 图片下载器:该项目中有一个线程负责下载网络上的图片,并将下载完成的图片保存在本地。
同时,每当有图片下载完成后,会通知另一个线程更新界面,以显示下载进度和已完成的图片。
4. 多线程爬虫:这是一个经典的多线程项目案例,通过多线程的方式来实现对网站的并行爬取。
每个线程负责爬取不同的网页,并将爬取结果存储起来,最后合并所有结果。
5. 多线程排序:这个项目中,一个线程负责生成一组随机数,而其他线程则负责对这组随机数进行排序。
可以使用不同的排序算法,比如冒泡排序、插入排序、选择排序等。
每个线程独立地对一部分数据进行排序,最后将各个线程的排序结果进行合并。
6. 多线程游戏:在一个多线程游戏项目中,可以有多个线程同时进行游戏逻辑的处理,比如玩家移动、攻击、技能释放等。
每个线程负责不同的游戏逻辑,通过并行执行来提高游戏的并发性和响应速度。
producer consumer 设计模式生产者消费者设计模式是一种多线程设计模式,用于解决生产者和消费者之间的同步和通信问题。
此设计模式基于一个共享缓冲区,生产者向缓冲区中放入数据,消费者从缓冲区中取出数据。
此设计模式能够保证生产者和消费者的同步,避免生产者在缓冲区已满时继续生产,消费者在缓冲区为空时继续消费的问题。
在生产者消费者设计模式中,有以下几个核心角色:1. 生产者:负责生成数据,并将数据放入缓冲区。
2. 消费者:负责从缓冲区中取出数据并进行消费。
3. 缓冲区:共享的数据结构,用于存储生产者生成的数据,以及供消费者取出的数据。
4. 消费者队列:一个队列用于存放等待消费者消费的数据。
5. 信号量:用于控制生产者和消费者的访问权限。
当缓冲区已满时,生产者需要等待信号量;而当缓冲区为空时,消费者需要等待信号量。
6. 锁:用于确保同时只有一个线程能够访问共享资源。
以下是生产者消费者设计模式的基本实现步骤:1. 初始化缓冲区、信号量和锁。
2. 创建生产者线程和消费者线程,并启动它们。
3. 生产者线程生成数据,并将数据放入缓冲区。
如果缓冲区已满,生产者线程会等待信号量。
4. 消费者线程从缓冲区中取出数据并进行消费。
如果缓冲区为空,消费者线程会等待信号量。
5. 当生产者线程将数据放入缓冲区后,释放信号量,允许消费者线程继续。
6. 当消费者线程从缓冲区中取出数据后,释放信号量,允许生产者线程继续。
通过使用生产者消费者设计模式,可以有效地解决多线程环境下的数据同步和通信问题,确保生产者和消费者之间的合作顺利进行。
多线程经典题目多线程经典题目主要涉及多线程编程的基本概念、同步机制、线程池、并发编程等方面。
以下是一些典型的多线程经典题目:1. 创建一个多线程程序,实现交替打印字符。
可以使用synchronize 同步锁、lock 锁、concurrent 的默认机制、volatile 关键字、Thread.sleep()/Thread.yield 机制、atomic 原子类等方式实现。
2. 实现一个多线程程序,打印0 和奇偶数字。
可以使用锁(sychronized 和Lock)、并发工具(barrier、semaphore)、CAS、Thread.sleep()/volatile、blocking queue 等方式实现。
3. 设计一个生产者-消费者问题的多线程程序。
可以使用ReentrantLock、full 和empty 对象、死循环等方式实现。
4. 实现一个哲学家进餐问题的多线程程序。
一个经典的并发控制问题,通过筷子数量和状态来控制哲学家的就餐行为。
5. 实现一个读者-写者问题的多线程程序。
使用两个锁(readerLock 和writerLock)来控制读写操作。
6. 设计一个多线程并发统计文件夹内关键字频率的程序。
可以使用线程池和Future 进行文件夹内关键字统计。
7. 实现一个多线程爬虫程序,爬取网页信息并处理。
可以使用线程池、并发集合等技术进行优化。
8. 设计一个多线程网络通信程序,实现客户端与服务器的通信。
可以使用阻塞IO、非阻塞IO、异步IO 等方式实现。
以上是一些典型的多线程经典题目,可以帮助你更好地理解和掌握多线程编程的基本知识和应用。
在实际编程过程中,多线程编程可能会遇到死锁、竞争条件、活锁等问题,需要根据具体场景选择合适的同步机制和线程池大小等参数来避免这些问题。
Java实现⽣产者消费者问题与读者写者问题摘要: Java实现⽣产者消费者问题与读者写者问题1、⽣产者消费者问题⽣产者消费者问题是研究多线程程序时绕不开的经典问题之⼀,它描述是有⼀块缓冲区作为仓库,⽣产者可以将产品放⼊仓库,消费者则可以从仓库中取⾛产品。
解决⽣产者/消费者问题的⽅法可分为两类:(1)采⽤某种机制保护⽣产者和消费者之间的同步;(2)在⽣产者和消费者之间建⽴⼀个管道。
第⼀种⽅式有较⾼的效率,并且易于实现,代码的可控制性较好,属于常⽤的模式。
第⼆种管道缓冲区不易控制,被传输数据对象不易于封装等,实⽤性不强。
同步问题核⼼在于:如何保证同⼀资源被多个线程并发访问时的完整性。
常⽤的同步⽅法是采⽤信号或加锁机制,保证资源在任意时刻⾄多被⼀个线程访问。
Java语⾔在多线程编程上实现了完全对象化,提供了对同步机制的良好⽀持。
在Java中⼀共有五种⽅法⽀持同步,其中前四个是同步⽅法,⼀个是管道⽅法。
wait() / notify()⽅法await() / signal()⽅法BlockingQueue阻塞队列⽅法Semaphore⽅法PipedInputStream / PipedOutputStream1.1 wait() / notify()⽅法wait() / nofity()⽅法是基类Object的两个⽅法,也就意味着所有Java类都会拥有这两个⽅法,这样,我们就可以为任何对象实现同步机制。
wait()⽅法:当缓冲区已满/空时,⽣产者/消费者线程停⽌⾃⼰的执⾏,放弃锁,使⾃⼰处于等等状态,让其他线程执⾏。
notify()⽅法:当⽣产者/消费者向缓冲区放⼊/取出⼀个产品时,向其他等待的线程发出可执⾏的通知,同时放弃锁,使⾃⼰处于等待状态。
各起了4个⽣产者,4个消费者package test;public class Hosee {private static Integer count = 0;private final Integer FULL = 10;private static String LOCK = "LOCK";class Producer implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(3000);} catch (Exception e) {e.printStackTrace();}synchronized (LOCK) {while (count == FULL) {try {LOCK.wait();} catch (Exception e) {e.printStackTrace();}}count++;System.out.println(Thread.currentThread().getName() + "⽣产者⽣产,⽬前总共有" + count);LOCK.notifyAll();}}}}class Consumer implements Runnable {@Overridepublic void run() {Thread.sleep(3000);} catch (InterruptedException e1) {e1.printStackTrace();}synchronized (LOCK) {while (count == 0) {try {LOCK.wait();} catch (Exception e) {}}count--;System.out.println(Thread.currentThread().getName() + "消费者消费,⽬前总共有" + count);LOCK.notifyAll();}}}}public static void main(String[] args) throws Exception {Hosee hosee = new Hosee();new Thread(hosee.new Producer()).start();new Thread(hosee.new Consumer()).start();new Thread(hosee.new Producer()).start();new Thread(hosee.new Consumer()).start();new Thread(hosee.new Producer()).start();new Thread(hosee.new Consumer()).start();new Thread(hosee.new Producer()).start();new Thread(hosee.new Consumer()).start();}}(需要注意的是,⽤什么加锁就⽤什么notify和wait,实例中使⽤的是LOCK)部分打印结果:由于⽣产者和消费者说明⼀致,所以最多都是在2左右,当减少⼀个消费者时,则会加到10。
目录1.需求分析 (1)1.1 课程设计题目 (1)1.2 课程设计任务 (1)1.3 课程设计原理 (1)1.4 课程设计要求 (1)1.5 实验环境 (1)2. 概要设计 (2)2.1 课程设计方案概述 (2)2.2 课程设计流程图 (2)3.详细设计 (3)3.1 主程序模块 (3)3.2 生产者程序模块 (4)3.3 消费者程序模块 (5)4.调试中遇到的问题及解决方案 (5)5.运行结果 (6)6.实验小结 (7)参考文献 (7)附录:源程序清单 (7)1.需求分析1.1 课程设计题目用多线程同步方法解决生产者-消费者问题1.2 课程设计任务(1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者/消费者线程的标识符。
(2)生产者和消费者各有两个以上。
(3)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。
1.3 课程设计原理生产者和消费者问题是从操作系统中的许多实际同步问题中抽象出来的具有代表性的问题,它反映了操作系统中典型的同步例子,生产者进程(进程由多个线程组成)生产信息,消费者进程使用信息,由于生产者和消费者彼此独立,且运行速度不确定,所以很可能出现生产者已产生了信息而消费者却没有来得及接受信息这种情况。
为此,需要引入由一个或者若干个存储单元组成的临时存储区(即缓冲区),以便存放生产者所产生的信息,解决平滑进程间由于速度不确定所带来的问题。
1.4 课程设计要求(1)有界缓冲区内设有20个存储单元,放入/取出的数据项设定为1~20这20个整型数。
(2)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、当前指针位置和生产者/消费者线程的标识符。
(3)生产者和消费者各有两个以上。
(4)多个生产者或多个消费者之间须共享对缓冲区进行操作的函数代码。
1.5 实验环境系统平台:LINUX开发语言:C开发工具:PC机一台2. 概要设计2.1 课程设计方案概述本设计中设置一个长度为20的一维数组buff[20]作为有界缓冲区,缓冲区为0时,代表该缓冲区内没有产品,buff[i]=i+1表示有产品,产品为i+1。
设置3个同步信号灯:一个说明空缓冲区的数目,用empty表示,其初值为有界缓冲区的大小20 ;另一个说明满缓冲区(即产品)的数目,用full表示,其初值为0。
由于缓冲区是临界资源,必须互斥使用,所以还设置了一个互斥信号灯mutex,其初值为1。
用这3个信号灯有效控制多个生产者线程和多个消费者线程的同步准确的运行。
Main()函数中调用函数sem_init()对信号灯进行初始化;利用for语句循环创建5个producer(生产者)分离线程和5个consumer(消费者)分离线程;Producer线程通过调用函数sem_wait(&empty)判断是否有空缓冲区。
若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可将产品放入缓冲区,及时输出缓冲区里的内容。
然后依次调用函数sem_post(&mutex)和sem_post(&full)来释放缓冲区操作权和增加满缓冲区信号量的值;Consumer线程通过调用函数sem_wait(&full) 判断是否有满缓冲区。
若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可从满缓冲区中取出产品消费,并及时输出缓冲区里的内容。
然后依次调用函数sem_post(&mutex)和sem_post(&emptyl)来释放缓冲区操作权和增加空满缓冲区信号量的值。
Producer和Consumer线程中均用缓冲区指针b指出当前是对哪一个缓冲区进行放入/取出操作;并调用pthread_self()函数来显示其自身的标识符。
2.2 课程设计流程图设计中主要有三个模块,首先从主程序中进入,完成初始化及创建好生产者和消费者线程后,进入生产者或消费者模块,具体流程见程序设计流程图如图1所示:生产者消费者3.详细设计3.1 主程序模块主程序中利用函数sem_init()对信号灯进行初始化;利用for语句循环创建3个producer(生产者)分离线程和3个consumer(消费者)分离线程,程序段如下:int main(void){ int i;initbuff();for(i=0;i<5;i++){pthread_create(&id1[i],NULL,(void *)producer,(&i));} //创建生产者线程for(i=0;i<5;i++){pthread_create(&id2[i],NULL,(void *)consumer,(&i));} //创建消费者线程for(i=0;i<5;i++){ pthread_join(id1[i],NULL);pthread_join(id2[i],NULL); }exit(0); } //等待生产者线程,消费者线程,结束主线程3.2 生产者程序模块Producer线程通过调用函数sem_wait(&empty)判断是否有空缓冲区。
若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可将产品放入缓冲区,及时输出缓冲区里的内容;并用指针b指出当前是对哪一个缓冲区进行放入/取出操作,且调用pthread_self()函数来显示其自身的标识符。
然后依次调用函数sem_post(&mutex)和sem_post(&full)来释放缓冲区操作权和增加满缓冲区信号量的值,程序段如下:void producer() //生产者{ int pid=0;int j,k;pid=pthread_self(); //获得生产者标识符while(1){ for(j=0;j<5;j++){ if(pid==id1[j]){ k=j+1; } }sem_wait(&empty); //P操作,判断缓冲区是否有空位置sem_wait(&mutex); //P操作,获得对临界资源的操作权if(p%21!=0){ buff[b]=p%21;printf("producer %d produce:%d\n",k,p%21);printbuff();b++; p++; }else p++;sem_post(&mutex); //V操作,释放临界资源sem_post(&full); //V操作,满缓冲区信号灯加1sleep(4); } }3.3 消费者程序模块Consumer线程调用调用函数sem_wait(&full) 判断是否有满缓冲区。
若无,则阻塞当前线程,若有则调用函数sem_wait(&mutex)等待对临界资源的操作权,当mntex为1时,便获得临界资源的操作权,可从满缓冲区中取出产品消费,并及时输出缓冲区里的内容,并用指针b指出当前是对哪一个缓冲区进行放入/取出操作,且调用pthread_self()函数来显示其自身的标识符。
然后依次调用函数sem_post(&mutex)和sem_post(&emptyl)来释放缓冲区操作权和增加空满缓冲区信号量的值,程序段如下:void consumer() //消费者{ int cid=0;int j,k;cid=pthread_self(); //获得消费者标识符while(1){ for(j=0;j<5;j++){ if(cid==id2[j]){ k=j+1; } }sem_wait(&full); //P操作,判断缓冲区是否已满sem_wait(&mutex); //P操作,获得对临界资源的操作权if(c%21!=0){ c1=buff[b-1];printf("consumer %d consume:%d\n",k,c1);buff[b-1]=0;printbuff();b--; c++; }else c++;sem_post(&mutex); //V操作,释放临界资源sem_post(&empty); //V操作,释放一个空位置sleep(6); }4.调试中遇到的问题及解决方案在调试过程中主要遇到三个问题:1.在设计刚完成的时候,我并没有用pid=pthread_self()和cid=pthread_self()来获得生产者和消费者的标识符,所以运行的结果是生产者和消费者的标识符都是随机的,后来问同学才找到这种调用方法。
2.我想让生产者无限的生产,消费者无限的消费,所以就用了一个条件判断语句,判断的条件是p%21!=0,而我写成了p/21!=0,所以运行的结果是生产者的生产是混乱的,没有规律。
3.刚开始我的运行结果还有一个问题就是生产者生产超过21的产品后,产品号不是1,2…等,而是21,22,…,后来检查后才发现printf("producer %d produce:%d\n",k,p%21)中的产品号不是p%21,所以出现那种情况。
5.运行结果显示结果如下:(部分运行结果)6.实验小结这次课程设计主要针对的是操作系统中的经典问题即:生产者——消费者问题使用多线程控制,刚拿到设计题目时心里其实挺没底的,因为之前我对线程不是很了解,不过幸好老师上课的时候给了我们一个关于Linux多线程编程的文档,里面详细的介绍了线程的一些基本命令,所以我在这次设计中基本上都用的是文档里的命令。
从这次课程设计中,我对线程的概念以及一些命令都有了一定的理解和掌握,并且也知道了它与进程之间的区别,除此之外,通过这次课程设计让我对操作系统中的经典问题,生产者——消费者问题有了更深的理解,在做设计的过程中,也对上课的内容加深了理解并进行了巩固。
参考文献[1]庞丽萍.操作系统原理.华中科技大学出版社.2009年1月。
[2] 蒋静徐志伟.操作系统原理技术与编程[M].北京:机械工业出版社,2004[3]张红光李福才.UNIX操作系统。
机械工业出版社。
2006年1月[4]汤子瀛等.计算机操作系统.西安电子科技大学出版社.2001年5月[5]付国瑜杨武周敏.计算机操作系统原理及应用上机实验指导.重庆工学院计算机学院.2005年1月.附录:源程序清单#include<stdio.h>#include<pthread.h>#include<stdlib.h>#include<semaphore.h>int buff[20]={0}; //有界缓冲区定义int b=0; //缓冲区的输出指针int p=1;int c=1;int c1=0; //消费数据变量sem_t full; //缓冲区的数量信号灯sem_t empty; //缓冲区满信号灯sem_t mutex; //互斥信号灯pthread_t id1[5];pthread_t id2[5];void initbuff() //初始化信号灯{ sem_init(&full,0,0);sem_init(&empty,0,20);sem_init(&mutex,0,1); //初始化互斥信号灯 } void printbuff() //打印缓冲区{ int j;printf("datas in buff are:");for(j=0;j<20;j++)printf("%d ",buff[j]);printf("\n"); }void producer() //生产者{ int pid=0;int j,k;pid=pthread_self();while(1){ for(j=0;j<5;j++){ if(pid==id1[j]){ k=j+1; } }sem_wait(&empty);sem_wait(&mutex);if(p%21!=0){ buff[b]=p%21;printf("producer %d produce:%d\n",k,p%21);printbuff();b++;p++; }else p++;sem_post(&mutex);sem_post(&full);sleep(4); } }void consumer() //消费者{ int cid=0;int j,k;cid=pthread_self();while(1){ for(j=0;j<5;j++){ if(cid==id2[j]){ k=j+1; } }sem_wait(&full); //看是否有数据在缓冲区sem_wait(&mutex); //临界区if(c%21!=0){ c1=buff[b-1];printf("consumer %d consume:%d\n",k,c1);buff[b-1]=0;printbuff();b--;c++; }else c++;sem_post(&mutex);sem_post(&empty);sleep(6); } }int main(void){ int i;initbuff();for(i=0;i<5;i++){pthread_create(&id1[i],NULL,(void *)producer,(&i));} //创建生产者线程 for(i=0;i<5;i++){pthread_create(&id2[i],NULL,(void *)consumer,(&i));} //创建消费者线程 for(i=0;i<5;i++){ pthread_join(id1[i],NULL);pthread_join(id2[i],NULL); }exit(0); } //等待生产者线程,消费者线程,结束主线程武汉理工大学华夏学院《操作系统原理》课程设计报告10。