文章目录
前言
一、开发语言及实验平台或实验环境
二、实验目的
三、实验要求
四、实验原理
五、实验过程
六、代码详解
七、diy一下
总结
前言
计算机操作系统是一门研究计算机系统的基本原理和设计方法的课程,它涉及到计算机系统的结构、功能、性能和管理等方面。操作系统实验是操作系统课程的重要组成部分,它可以帮助加深对操作系统理论知识的理解,提高分析和解决实际问题的能力,培养动手实践的能力和创新精神。
进程同步控制是操作系统实验中的一个重要内容,它主要涉及到进程间的协作、竞争和互斥等问题,以及如何使用信号量、管程、消息传递等机制来实现进程同步控制的方法。本实验旨在让学生通过编写和运行一些典型的进程同步控制程序,掌握进程同步控制的基本概念、原理和技术,熟悉操作系统提供的进程同步控制接口和工具,增强对操作系统内核功能的认识和理解。
一、开发语言及实验平台或实验环境
C++/JAVA
Turbo C / Microsoft Visual Studio 6.0 / Microsoft Visual Studio .NET 2010
本文中使用的语言是Java,使用的平台是idea
二、实验目的
(1)加强对进程概念的理解,尤其是对进程的同步与互斥机制的理解。
(2)分析进程竞争资源的现象,学习解决进程互斥与同步的方法。
三、实验要求
(1)理解利用进程控制机制;
(2)理解利用信号量进行进程同步控制原理;
(3)使用某种编程语言进行模拟实现生产者-消费者进程。
四、实验原理
(注意:这个仅是个例子,仅供参考)
生产者-消费者问题描述的是:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能够并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可以从一个缓冲区中取走产品去消费。尽管所有的生产者和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品;也不允许生产者进程向一个已经装满产品的缓冲区中投放产品。
这是一个同步与互斥共存的问题。
生产者—消费者问题是一个同步问题。即生产者和消费者之间满足如下条件:
(1) 消费者想接收数据时,有界缓冲区中至少有一个单元是满的。
(2) 生产者想发送数据时,有界缓冲区中至少有一个单元是空的。
五、实验过程
浅画一个流程图:
示例代码如下:
class Q
{
String name;
int num=0;
int size=10;
}
class Producer implements Runnable
{
Q q;
Producer(Q q)
{
this.q = q;
this.q.name="producer";
}
public void run()
{
while(true)
{
synchronized(q)
{
if(q.num<q.size)
{
q.num++;
System.out.println("producer已生产第:"+q.num+"个产品!");
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
q.notify();
}
else
{
try {
System.out.println("producer stop!");
q.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer implements Runnable
{
Q q;
Consumer(Q q)
{
this.q = q;
this.q.name="consumer";
}
public void run()
{
while(true)
{
synchronized(q)
{
if(q.num>0)
{
System.out.println("consumer要消费第:"+q.num+"个产品!");
q.num--;
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
q.notifyAll();
}
else
{
try {
System.out.println("consumer stop!");
q.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class project
{
public static void main(String[] args)
{
Q q = new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
效果如下:
六、代码详解
这段代码定义了三个类:Q
,Producer
和Consumer
。
Q
类定义了一个名为name
的字符串变量,一个名为num
的整数变量,初始值为0,以及一个名为size
的整数变量,初始值为10。
Producer
类实现了Runnable
接口,它有一个构造函数,该构造函数接受一个Q
类型的参数。在构造函数中,它将传递的参数赋值给成员变量q
,并将其成员变量name
设置为“producer”。
在Producer
类中,还定义了一个名为run()
的方法。这个方法是一个无限循环,在循环中,它使用synchronized(q)
块来确保一次只有一个线程可以访问共享的Q
对象。在synchronized(q)
块中,它首先检查共享的Q
对象中的变量num
是否小于其变量size
。如果是,则将其变量num
增加1,并打印一条消息表示生产了一个产品。然后它调用线程休眠100毫秒,并使用q.notify()
唤醒等待该对象锁的线程。
如果共享的
Q
对象中的变量num
不小于其变量size
,则打印一条消息表示生产者停止,并调用q.wait()
使当前线程等待,直到其他线程调用该对象的notify()
或notifyAll()
方法。
Consumer
类也实现了Runnable
接口,它有一个构造函数,该构造函数接受一个Q
类型的参数。在构造函数中,它将传递的参数赋值给成员变量q
,并将其成员变量name
设置为“consumer”。
在Consumer
类中,也定义了一个名为run()
的方法。这个方法也是一个无限循环,在循环中,它也使用synchronized(q)
块来确保一次只有一个线程可以访问共享的Q
对象。在synchronized(q)
块中,它首先检查共享的Q
对象中的变量num
是否大于0。如果是,则打印一条消息表示要消费一个产品,并将其变量num
减少1。然后它调用线程休眠100毫秒,并使用
q.notifyAll()
唤醒等待该对象锁的所有线程。
如果共享的Q
对象中的变量num
不大于0,则打印一条消息表示消费者停止,并调用q.wait()
使当前线程等待,直到其他线程调用该对象的notify()
或notifyAll()
方法。
最后,project
类定义了一个名为main()
的方法。在这个方法中,它创建了一个新的Q
对象,并使用这个对象创建了一个新的Producer
对象和一个新的Consumer
对象。然后它分别为这两个对象创建了一个新的线程,并启动了这两个线程。
七、diy一下
改成信号量机制:
import java.util.concurrent.Semaphore;
public class os1_1_1 {
static class Q {
String name;
int num = 0;
int size = 10;
Semaphore empty = new Semaphore(size);
Semaphore full = new Semaphore(0);
Semaphore mutex = new Semaphore(1);
}
static class Producer implements Runnable {
Q q;
Producer(Q q) {
this.q = q;
this.q.name = "producer";
}
public void run() {
while (true) {
try {
q.empty.acquire();
q.mutex.acquire();
q.num++;
System.out.println("producer已生产第:" + q.num + "个产品!");
Thread.currentThread().sleep(100);
q.mutex.release();
q.full.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer implements Runnable {
Q q;
Consumer(Q q) {
this.q = q;
this.q.name = "consumer";
}
public void run() {
while (true) {
try {
q.full.acquire();
q.mutex.acquire();
System.out.println("consumer要消费第:" + q.num + "个产品!");
q.num--;
Thread.currentThread().sleep(100);
q.mutex.release();
q.empty.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Q q = new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
这段代码中,我们使用了三个信号量:empty
,full
和mutex
。empty
表示空缓冲区的数目,初始值为有界缓冲区的大小,即size
。full
表示已用缓冲区的数目,初始值为0。mutex
是一个互斥信号量,用于确保生产者和消费者线程之间的互斥执行,初始值为1。
在生产者线程中,我们首先调用q.empty.acquire()
来获取信号量。如果成功获取信号量,则调用q.mutex.acquire()
来获取互斥信号量。如果成功获取互斥信号量,则执行生产操作。完成生产操作后,我们调用q.mutex.release()
来释放互斥信号量,并调用q.full.release()
来释放消费者信号量,以便消费者线程可以执行消费操作。
在消费者线程中,我们首先调用q.full.acquire()
来获取信号量。如果成功获取信号量,则调用q.mutex.acquire()
来获取互斥信号量。如果成功获取互斥信号量,则执行消费操作。完成消费操作后,我们调用q.mutex.release()
来释放互斥信号量,并调用q.empty.release()
来释放生产者信号量,以便生产者线程可以执行生产操作。
看看效果:
改成c语言形式:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define SIZE 10
typedef struct {
char *name;
int num;
int size;
sem_t empty;
sem_t full;
sem_t mutex;
} Q;
void *producer(void *arg) {
Q *q = (Q *)arg;
while (1) {
sem_wait(&q->empty);
sem_wait(&q->mutex);
q->num++;
printf("producer已生产第:%d个产品!\n", q->num);
sleep(1);
sem_post(&q->mutex);
sem_post(&q->full);
}
}
void *consumer(void *arg) {
Q *q = (Q *)arg;
while (1) {
sem_wait(&q->full);
sem_wait(&q->mutex);
printf("consumer要消费第:%d个产品!\n", q->num);
q->num--;
sleep(1);
sem_post(&q->mutex);
sem_post(&q->empty);
}
}
int main() {
Q q = {.name = "Q", .num = 0, .size = SIZE};
sem_init(&q.empty, 0, SIZE);
sem_init(&q.full, 0, 0);
sem_init(&q.mutex, 0, 1);
pthread_t producer_thread, consumer_thread;
pthread_create(&producer_thread, NULL, producer, &q);
pthread_create(&consumer_thread, NULL, consumer, &q);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
return 0;
}
这段代码中,我们定义了一个名为Q
的结构体,它包含了一个名为name
的字符串指针,一个名为num
的整数变量,一个名为size
的整数变量,以及三个信号量:empty
,full
和mutex
。
在主函数中,我们初始化了一个Q
类型的变量,并使用sem_init()
函数初始化了它的三个信号量。然后我们创建了两个线程:生产者线程和消费者线程,并分别调用了producer()
和consumer()
函数。
在生产者线程中,我们首先调用sem_wait()
函数来获取信号量。如果成功获取信号量,则调用sem_wait()
函数来获取互斥信号量。如果成功获取互斥信号量,则执行生产操作。完成生产操作后,我们调用sem_post()
函数来释放互斥信号量,并调用sem_post()
函数来释放消费者信号量,以便消费者线程可以执行消费操作。
在消费者线程中,我们首先调用sem_wait()
函数来获取信号量。如果成功获取信号量,则调用sem_wait()
函数来获取互斥信号量。如果成功获取互斥信号量,则执行消费操作。完成消费操作后,我们调用sem_post()
函数来释放互斥信号量,并调用sem_post()
函数来释放生产者信号量,以便生产者线程可以执行生产操作。文章来源:https://www.toymoban.com/news/detail-754583.html
总结
这篇文章中所述的实验是从网页内容中总结的。它是一个关于进程同步控制的实验,目的是让学生理解进程同步的概念和方法,以及掌握java或c语言的多线程编程技巧。实验要求学生模拟一个生产者-消费者问题,使用信号量机制来实现进程间的同步和互斥。实验设计了一个缓冲区类,一个生产者类,一个消费者类,以及一个主类来控制程序的运行。实验结果显示,程序能够正确地运行,并且没有出现死锁或数据丢失的情况。文章来源地址https://www.toymoban.com/news/detail-754583.html
到了这里,关于操作系统实验:进程同步控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!