0%

操作系统——进程同步——生产者消费者问题

进程同步

  • pv 操作 互斥锁

    1
    2
    3
    4
    P --wait(信号量S){
    S<=0
    S-- //上锁
    }

    成对出现

    1
    2
    3
    V --signal(信号量){
    S++ //开锁
    }
  • 生产者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int in(),out=();
item butter[n];
semaphore mutex=1,empty=n,full=();//mutex必须为1 ,empty空位置,full有多少个
void proceducer(){
do{
producer an item nextp;
……
wait(empty); //enpty--
wait(mutex); //上锁
butter[in]=nextp; //生产一个
in:=(in+1)%n; // 生产一个
signal(mutex); //解锁
signal(full); //full++
}while(TRUE);
}
  • 消费者
1
2
3
4
5
6
7
8
9
10
11
void consumer(){
do{
wait(full); //full-1
wait(mutex); //上锁
nextc=butter[out]; //消费一个
out=(out+1)%n; //消费一个
signal(mutex); //解锁
signal(empty); //empty++
consumer the item in nextc;
}while(TRUE);
}
1
2
3
4
5
void main(){
cobegin
proceducer();consumer();
coend
}

1679017003280

第1关:生产者消费者同步控制

任务描述

本关任务:编写生产者消费者同步控制程序。

相关知识

为了完成本关任务,你需要掌握:1.理解生产者消费者同步问题,2.线程的编写方法,3.使用信号量实现线程间同步控制。

线程并发引起的同步问题

线程以并发形式运行,当并发的线程间访问共享数据时,会发后争用现象,不进行同步控制的线程运行会造成不恰当的结果。

生产者消费者同步问题

生产者消费者是典型的同步问题,他们共享了一个缓冲池(全局变量数组),当缓冲池有空位时生产者线程向缓冲池中依次赋值,如果缓冲池满则等待。当缓冲池中有数据时消费者线程从中取走数据,如果缓冲池空则等待。

如何解决线程间访问共享变量的冲突问题

当多个线程访问同一个共享变量时,共享变量成为临界资源,它需要操作系统提供同步控制机制,以保证多个线程可以依序访问,当一个线程操作临界资源时其它线程不会中断其操作,因此对临界资源的操作是安全的。 下面是linux平台使用信号量的头文件和方法。 #include <semaphore.h> int sem_init(sem_t * sem, int pshared, unsigned int value);//创建信号量变量,value是信号量的初值 int sem_destroy(sem_t *sem);//销毁信号量 int sem_post(sem_t * sem); //信号量值增加1,并激活处于等待状态的线程 int sem_wait(sem_t * sem); //信号量值减少1 为0时将调用该方法的线程被OS阻塞

编程要求

主线程序启动生产者线程和消费者线程,并等待两个线程的结束。 两个线程使用三个信号量,其中生产者线程向全局变量进行十次赋值(代表生产),每次赋值前都要检查是否有空位,有空位的情况下 需要获得互斥量sem_mutex,并对共享缓冲区进行赋值,然后释放互斥量以便消费者线程可以操作缓冲区临界资源。 消费者线程要从缓冲区取值,取值前先检查是否有可用数据,有可用数据的情况下再获得互斥量sem_mutex,再取出缓冲区的值。 要特别说明的是生产者和消费者线程工作的速率是不同的,但在同步信号控制下,两个线程节奏互相配合步调一致。在《计算机操作系统》 教材中使用了伪代码,并且循环体没有结束条件,在此用C语言改写原程序,生产者一共只生产十个数据。 ####测试说明 为简化数据操作,程序没有输入,主线程已经写好, void * producer(void * arg); void * consumer(void * arg); static sem_t sem_empty;//空位个数 static sem_t sem_full; //可用数据个数 static sem_t sem_mutex; //互斥量,用于控制两个线程互斥访问缓冲区 static int buffer[]={0,0,0}; int main(int argc,char * argv[]) { pthread_t id_prod,id_consum; sem_init(&sem_empty,0,3); //初值为3,空位为3, sem_init(&sem_full,0,0); //初值为0,可用数据个数为0, sem_init(&sem_mutex,0,1); //初值为1,用于控制两个线程互斥访问缓冲区 pthread_create(&id_prod, NULL,producer,NULL);//创建生产者线程 pthread_create(&id_consum,NULL,consumer,NULL);//创建消费者线程

pthread_join(id_prod,NULL); //主线程等待生产者线程结束 pthread_join(id_consum,NULL);//主线程等待消费者线程结束 sem_destroy(&sem_empty); sem_destroy(&sem_full); sem_destroy(&sem_mutex); return 0; } 学生需要补充producer线程与consumer中关于同步控制部分的程序。

输出:1至10 (需由学生根据程序生成正确结果) 开始你的任务吧,祝你成功!

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
//stu001.c 生产者消费者同步控制,由学生完成缺失代码。 
//主线程序启动生产者线程和消费者线程,全局变量初值为0
//生产者线程向全局变量进行10次赋值(代表生产),消费者线程从全局变量读取值,///并重新赋值0(代表消费了产品)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

void * producer(void * arg);
void * consumer(void * arg);
static sem_t sem_empty;//空位个数
static sem_t sem_full; //可用数据个数
static sem_t sem_mutex; //互斥量,用于控制两个线程互斥访问缓冲区
static int buffer[]={0,0,0};//共享的缓冲区
int main(int argc,char * argv[])
{
pthread_t id_prod,id_consum;
sem_init(&sem_empty,0,3); //初值为3,空位为3,
sem_init(&sem_full,0,0); //初值为0,可用数据个数为0,
sem_init(&sem_mutex,0,1); //初值为1,用于控制两个线程互斥访问缓冲区
pthread_create(&id_prod, NULL,producer,NULL);//创建生产者线程
pthread_create(&id_consum,NULL,consumer,NULL);//创建消费者线程

pthread_join(id_prod,NULL); //主线程等待生产者线程结束
pthread_join(id_consum,NULL);//主线程等待消费者线程结束
sem_destroy(&sem_empty);
sem_destroy(&sem_full);
sem_destroy(&sem_mutex);
return 0;
}
void * producer(void * arg)
{
int i,pIndex=0;
for(i=11;i<21;i++)
{
//请在begin end语句间补全程序语句实现生产者向缓冲区赋值操作
/* begin *******************程序代码约六行******************************* */
sem_wait(&sem_empty);//empty--
sem_wait(&sem_mutex); //枷锁
buffer[pIndex]=i; //缓冲区只有三个位置
pIndex=(pIndex+1)%3;// 11-- 0 12--1 13--2 14--0 15--1 16--2
sem_post(&sem_mutex);//解锁
sem_post(&sem_full);//full++
/* end ************************************************************** */
}
return NULL;
}
void * consumer(void *arg)
{
int i,cIndex=0;
for(i=11;i<21;i++)
{
//请在begin end语句间补全程序语句实现扫描算法,算出总访问磁道数存入totaltracks变量
/* begin *******************程序代码约六行******************************* */
sem_wait(&sem_full);//full--
sem_wait(&sem_mutex);//枷锁
printf("%d,",buffer[cIndex]);
cIndex=(cIndex+1)%3;//与上面对应
sem_post(&sem_mutex);//解锁
sem_post(&sem_empty);//empty++

/* end ************************************************************** */
}
return NULL;
}

参考Linux下c++的东西

https://man7.org/linux/man-pages/man3/sem_wait.3.html

1
2
3
sem_wait是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。
如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码。
(不得不说,百度确实蛮会复制粘贴翻译的,只不过没有那个网站格式做的好hhh)
1
sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同 时对同一个文件进行读和写操作的两个程序就有可能会引起冲突。
1
sem_t  C语言中,信号量的数据类型为结构sem_t,它本质上是一个长整型的数。

https://blog.51cto.com/u_13999641/4314815

没看完【argc,argv是什么】

真的忘的差不多了0.0

1679026388457

1679026432795

-------------本文结束感谢您的阅读-------------
老板你好,讨口饭吃