上机2(操作系统上机).pdf

上传人:asd****56 文档编号:70322758 上传时间:2023-01-19 格式:PDF 页数:27 大小:311.52KB
返回 下载 相关 举报
上机2(操作系统上机).pdf_第1页
第1页 / 共27页
上机2(操作系统上机).pdf_第2页
第2页 / 共27页
点击查看更多>>
资源描述

《上机2(操作系统上机).pdf》由会员分享,可在线阅读,更多相关《上机2(操作系统上机).pdf(27页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、第四章 同步和互斥 现代操作系统的中心方案是多道程序设计、多处理和分布式处理,这些方案的基础以及操作系统设计的基础是并发。当多个进程并发执行时,不论是在多处理器系统的情况下,还是单处理器多道程序系统中,都会产生解决冲突和合作的问题。并发进程可以按照多种方式进行交互。互相之间不知道对方的进程可能需要竞争的资源,如处理器时间或对 I/O 设备的访问。进程间由于共享访问一个公共对象,如主存中的一块空间或一个文件,可能间接知道对方,这类交互中产生的重要问题是互斥和死锁。互斥指的是,对一组并发进程,一次只有一个进程能够访问一个给定的资源或执行一个给定的功能。互知技术可以用于解决诸如资源争用之类的冲突,还

2、可以用于进程间的同步,使得它们可以合作。后一种情况的一个例子是生产者/消费者模型。为解决互斥问题,已经开发了许多软件算法,其中最著名的是 Dekker 算法。这种方法的处理开销很高,并且产生逻辑错误的危险也很高。支持互斥的第二种方法涉及到专门的及其指令,这种方法减少了开销,但由于使用了忙等待,因而仍然是低效的。支持互斥的另一种方法是在操作系统中提供功能,其中最常见的两种技术是信号量和消息机制。信号量用于在进程间发送信号,并可以很容易的用于实施一个互斥规定。消息对实施互斥是很有用的,它还为进程间的通信提供了一种有效的方法。在 Unix 和 Linux 中,可以有几种方式来实现同步互斥:?互斥锁和

3、条件变量?读写锁?记录上锁?Posix 信号量?System V 信号量 本章主要讨论解决互斥问题的两种方法,一种是软件算法,一种是信号量实现。4 4.1 互斥算法 互斥算法 软件方法是为在一个处理器或者共享主存的处理器机器上执行的并发进程实现的。4.1.2 Peterson 算法 Peterson 算法 Dekker 算法解决了互斥问题,但相对比较复杂,关于正确性证明也需要一定的技巧。Peterson 提出了一种简单而优雅的方案。和 Dekker 算法一样,全局数组变量 flag 表示每个进程关于互斥的位置,全局变量 turn 解决同时冲突。#include#include#define f

4、alse 0#define true 1 int flag2;int turn;void P0()while(true)flag0=true;turn=1;while(flag1&turn=1);/临界区 flag0=false;/其余代码 void P1()while(true)flag1=true;turn=0;while(flag0&turn=0);/临界区 flag1=false;/其余代码 int main()flag0=false;flag1=false;parbegin(P0,P1);显然,互斥可以得到保证。考虑进程 P0。一旦它把 flag0设置为 ture,P1 不能进入其临

5、界区;如果 P1 已经在自己的临界区中,则 flag1true,且 P0 被阻止进入临界区。另一方面,还可以防止互相阻塞。假设 P0 在它的 while 循环被阻塞,这意味着 flag1为 true 且turn1,则当 flag1 变成 false 或者当 turn 变成 0 时,P0 都可以进入自己的临界区。现在穷举三种情况:1、P1 不想进入临界区。这种情况是不可能的,因为它已经表示了想进入(flag1=false)。2、P1 正在等待进入它的临界区。这种情况也不可能,因此如果 turn1,P1 就能够进入它的临界区。3、P1 正在重复使用它的临界区,从而垄断了对临界区的访问。这不可能发生

6、,因此P1 在每次试图进入自己的临界区之前都被迫把 turn 设置为 0,从而给了 P0 进入的机会。因此,我们对两个进程间的互斥问题给出了一种简单的解决方案。此外,Peterson 算法可以很容易的推广到 n 个进程的情况。4.2 信号量 4.2 信号量 4.2.1 信号量原语 4.2.1 信号量原语 信号量可以看作一个具有整数值的变量,在它之上定义三个操作:1.一个信号量可以初始化成非负数。2.wait 操作使信号量减 1。如果值变成负数,则执行 wait 的进程被阻塞。3.signal 操作使信号量加 1。如果值不是正数,则被 wait 操作阻塞的进程被解除阻塞。wait 和 signa

7、l 原语被假设是原子的,也就是说,它们不能被中断,每个例程被看作是一个不可分割的步骤。一个二元信号量的值只能是 0 或 1。下面分别给出信号量和二元信号量原语的定义。下面给出使用信号量进行互斥的一个例子。const int n=/*进程数*/;struct semaphore enum(zero,one)value;queueType queue;void wait(semaphore s)if(s.value=1)s.value=0;place this process in s.queue;block this process void signal(semaphore s)if(s.qu

8、eue.is_empty()s.value=1;else remove a process P from s.queue;place process P on ready list;二元信号量原语的定义二元信号量原语的定义 struct semaphore int count;queueType queue;void wait(semaphore s)s.count-;if(s.count0)place this process in s.queue;block this process void signal(semaphore s)s.count+;if(s.count=0)remove

9、a process P from s.queue;place process P on ready list;信号量原语的定义信号量原语的定义 semaphore s=1;void P(int i)while(true)wait(s);/临界区 signal(s);/其余部分 void main()parbegin(P(1),P(2),.,P(n):4.3 Linux4.3 Linux/Unix 提供的同步机制 提供的同步机制 4.3.1 互斥锁和条件变量 互斥锁和条件变量 互斥锁和条件变量出自 Posix 1 线程标准,它们可用来同步一个进程内的各个线程。如果一个互斥锁或条件变量存放在多个进

10、程间共享的某个内存区中,那么 Posix 还允许它用于这些进程间的同步。互斥锁用于保护临界区,以保证任何时刻只有一个线程在执行其中的代码,或者任何时刻只有一个进程在执行其中的代码。互斥锁用于上锁,条件变量则用于等待。定义一个互斥锁:static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;以下三个函数给一个互斥锁上锁和解锁:#include int pthread_mutex_lock(pthread_mutex_t*mptr);int pthread_mutex_trylock(pthread_mutex_t*mptr);int pthrea

11、d_mutex_unlock(pthread_mutex_t*mptr);如果尝试给一个已经由另外某个线程锁住的互斥锁上锁,那么 pthread_mutex_lock 将阻塞到该互斥锁解锁为止。pthread_mutex_trylock 是对应的非阻塞函数,如果该互斥锁已经锁住,它就返回一个 EBUSY 错误。条件变量是类型为 pthread_cond_t 的变量,以下两个函数用于这些变量。#include int pthread_cond_wait(pthread_cond_t*cptr,pthread_mutex_t*mpthr);int pthread_cond_signal(pthre

12、ad_cond_t*cptr);每个条件变量总是有一个互斥锁与之关联。当调用 pthread_cond_wait 等待某个条件为真时,同时指定其条件变量的地址和所关联的互斥锁的地址。互斥锁和条件变量可以静态分配并静态初始化,它们也可以动态分配,那要求动态的初始化它们。动态初始化允许指定进程间共享属性,从而允许在不同进程间共享某个互斥锁或者条件变量,其前提是该互斥锁或者条件变量必须存放在由这些进程共享的内存区中。4.3.2 Posix 信号量 4.3.2 Posix 信号量 信号量(semaphore)是一种用于提供不同进程间或者一个给定进程的不同线程间同步手段的原语。Posix 信号量就是其中

13、一种。Posix 信号量分为两种:?Posix 有名信号,使用 Posix IPC 名字标识,可用于进程或者线程间的同步。?Posix 基于内存的信号,存放在共享内存中,可用于进程或者线程间的同步。Posix 有名信号量总是能在不同进程之间共享,基于内存的信号量则必须在创建时指定是否在进程间共享。这两类信号量的持续性也有差别,有名信号量至少有随内核的持续性。基于内存的信号量则具有随进程的持续性。Posix 信号量是计数信号量,它提供以下三种基本操作:?创建一个信号量?等待一个信号量的值变为大于 0,然后将它的值减 1?给一个信号量的值加 1,并唤醒等待该信号量的任意线程,挂起该信号量 与互斥锁

14、相比较,Posix 信号量还有一个互斥锁没有提供的特性,即互斥锁必须总是由锁住它的线程解锁,信号量的挂出却不必由执行过它的线程执行。有关 Posix 信号量的具体库函数以及其用法,我们将以生产者消费者模型为例,在实验中详细给出。4 4.3.3 System V 信号量 System V 信号量 System V 信号量增加了技术信号量集这一概念,即一个或者多个信号量构成的集合,其中每个都是计数信号量。从 Posix 信号量到 System V 信号量发生了如下变动:1)System V 信号量由一组值构成。当指定应用到某个信号量集的一组信号量操作时,要么所有操作都执行,要么一个操作都不执行。2

15、)可应用到一个信号量集的每个成员的操作有三种:测试其值是否为 0,往其中加一个整数以及从其值中减调一个整数。Posix 信号量所允许的操作只是将其加 1 或者减 1。3)创建一个 System V 信号量集需要技巧,因为创建该集合并随后初始化其各个值需要两个操作,从而可能导致竞争状态。4)System V 信号量提供“复旧”特性,该特性保证进程终止时反转某个信号量操作。4.4 生产者/消费者模型 4.4 生产者/消费者模型 生产者/消费者问题是并发处理中最常见的一类问题。通常可以描述如下:有一个或者多个生产者产生某种类型的数据(记录、字符),并放置在缓冲区中;有一个消费者从缓冲区中取数据,每次

16、取一项;系统保证避免对缓冲区的重复操作,也就是说,在任何时候只有一个代理(生产者或者消费者)可以访问缓冲区。下面给出使用信号量、二元信号量来实现有限或无限缓冲区的生产者/消费者问题的模型。int n;binary_semaphore s=1;binary_semaphore delay=0;void producer()while(true)produce()waitB(s);append();n+;if(n=1)signalB(delay);signalB(s);void consumer()int m;waitB(delay);while(true)waitB(s);take();n-;m

17、=n;signalB(s);consume();if(m=0)waitB(delay);void main()n=0;parbegin(producer,consumer);图图 使用二元信号量解决无限缓冲区生产者使用二元信号量解决无限缓冲区生产者/消费者问题消费者问题 semaphore n=0;semaphore s=1;void producer()while(true)produce()wait(s);append();signal(s);signal(n);void consumer()while(true)wait(n);wait(s);take();signal(s);consu

18、me();void main()parbegin(producer,consumer);图图 使用信号量解决无限缓冲区生产者使用信号量解决无限缓冲区生产者/消费者问题消费者问题 const int sizeofbuffer=/*缓冲区大小*/semaphore n0;semaphore s=1;semaphore e=sizeofbuffer;void producer()while(true)produce()wait(e);wait(s);append();signal(s);signal(n);void consumer()while(true)wait(n);wait(s);take(

19、);signal(s);signal(e);consume();void main()parbegin(producer,consumer);图图 使用信号量解决有限缓冲区生产者使用信号量解决有限缓冲区生产者/消费者问题消费者问题 4.5 读者/写者模型 4.5 读者/写者模型 读写问题定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间,甚至可以是一组处理其寄存器;有一些只读取这个数据区的进程(reader)和一些只往数据区中写数据的进程(writer);此外还必须满足以下条件:1.可以同时有多个读进程读取关键区;2.在某个时刻,只能有一个写进程修改关键区;3.如

20、果一个写进程正在修改关键区时,禁止任何读进程不能读关键区;对于读写问题,还分为读优先、写优先两种模型。读优先读优先 读优先的要求是只要至少有一个读进程在读取关键区,写进程就不能获取关键区的控制权。它的算法模型如下:int readcount;semaphore x=1,wsem=1;void reader()while(true)wait(x);readcount+;if(readcount=1)wait(wsem);signal(x);READUNIT();wait(x);readcount-;if(readcount=0)signal(wsem);signal(x);void waiter

21、()while(true)wait(wsem);WRITEUNIT();signal(wsem);void main()readcount=0;parbegin(reader,writer);对于以上算法中各个信号量的解释如下表:信号量 用途 Wsem 只要有一个写进程在修改关键区,其他写进程,读进程就不能访问关键区 第一个读进程需要 wait(mutex),看能否进入关键区 最后一个读进程需要 signal(mutex),允许写进程进入关键区 X 全局变量 readcount 记录读进程数目,x 用来保护 radcount 互斥 写优先 写优先的要求是当一个写进程准备访问关键区时,不允许新的

22、读进程访问该关键区(即当处于这样一个状态,系统没有任何读进程,因此可以调用新的读进程,也可以调用新的写进程,但是这时要调度写进程而不能是读进程。写优先的算法模型如下所示:int readcount,writecount;semaphore x=1,y=1,z=1,wsem=1,rsem=1;void reader()while(true)wait(z);wait(rsem);wait(x);readcount+;if(readcount=1)wait(wsem);signal(x);signal(rsem);signal(z);READUNIT();wait(x);readcount-;if(

23、readcount=0)signal(wsem);signal(x);void waiter()while(true)wait(y);writecount+;if(writecount=1)wait(rsem);signal(y);wait(wsem);WRITEUNIT();signal(wsem);wait(y);writecount-;if(writecount=0)signal(rsem);signal(y);void main()readcount=0;writecount=0;parbegin(reader,writer);以上写优先算法模型中各个信号量的含义是:信号量 用途 Ws

24、em 只要有一个写进程在修改关键区,其他写进程,读进程就不能访问关键区 第一个读进程需要 wait(mutex),看能否进入关键区 最后一个读进程需要 signal(mutex),允许写进程进入关键区 X 全局变量 readcount 记录读进程数目,x 用来保护 radcount 互斥 Rsem 当至少有一个写进程准备访问数据区时,用于禁止所有的读进程 writecount 控制 rsem 的设置 Y 控制 writecount 的更新 Z z 在 rsem 上只允许一个读进程排队,其他读进程在 z 上排队,从而使得写进程可以跳过 rsem 以上写优先算法模型中进程队列的状态如下表:系统中只

25、有读进程?设置 mutex(wsem)?没有队列 系统中只有写进程?设置 wsem,rsem?写进程在 wsem 上排队 既有读进程又有写进程,读优先既有读进程又有写进程,读优先?由读进程设置 wsem?由写进程设置 rsem?所有写进程在 wsem 上排队?一个读进程在 rsem 上排队?其他读进程在 z 上排队 既有读进程又有写进程,写优先?由写进程设置 wsem?由写进程设置 rsem?写进程在 wsem 上排队?一个读进程在 rsem 上排队?其他读进程在 z 上排队 4.6 实验三 多线程实现 Peterson 算法4.6 实验三 多线程实现 Peterson 算法 实验题目:实验题

26、目:用 POSIX 的线程库(pthread_)实现 Peterson 算法 实验目的:实验目的:1)理解并发中的互斥原理 2)掌握 Peterson 算法 实验分析:实验分析:该题目比较简单,仅仅需要学生们将 Peterson 算法模型和 POSIX 的线程库(pthread 开头的函数)结合起来进行编程,使得在 linux 环境上实现、运行、验证 Peterson 算法。函数参考手册:函数参考手册:参见“线程控制”一章中的实验。上机练习:上机练习:Ex1:用 POSIX 的线程库(pthread_)实现 Peterson 算法。Ex2:用 POSIX 的线程库(pthread_)实现 De

27、kker 算法。4.7 实验四 用 Peterson 算法实现生产者4.7 实验四 用 Peterson 算法实现生产者/消费者 消费者 实验题目:实验题目:用 Peterson 算法实现生产者消费者模型 实验目的:实验目的:1)掌握 Peterson 算法 2)掌握生产者消费者模型 3)能够将 Peterson 算法和典型并发模型相结合,解决实际问题 实验分析:实验分析:1)根据本书前部分介绍的生产者消费者模型和 Peterson 算法,同学们仅需将二者很好的结合起来即可。2)本实验可用多进程、多线程实现,为了简洁性,本实验仅要求同学们用多线程实现即可。3)本实验仅要求一个生产者、一个消费者

28、即可。函数参考手册:函数参考手册:本实验要用到 POSIX 的线程库(pthread 开头的函数),具体库函数前面已有介绍,参见“线程控制”一章。上机练习:上机练习:Ex1:用 POSIX 的线程库(pthread_)、Peterson 算法实现生产者消费者模型。4.8 实验五 用互斥锁实现生产者4.8 实验五 用互斥锁实现生产者/消费者 消费者 实验题目:实验题目:用互斥锁实现生产者消费者模型 实验目的:实验目的:1)理解互斥锁和条件变量含义。2)掌握互斥锁和条件变量用法。3)掌握生产者消费者模型。实验分析:实验分析:1)实验用线程实现,从而生产者和消费者共享的关键区可以用全局变量实现。如果

29、用进程实现,则还需要用到共享内存等机制。2)实验要求用互斥锁和条件变量来实现同步互斥功能,从而取代 Peterson 软件算法实现方式,因此要用到 pthread_mutex_lock 等库函数,具体参考 pthread 线程库。3)实验时可以将关键区中信息输出到屏幕或者日志文件中,观察运行结果是否正确。4)在使用互斥锁时要注意:互斥锁必须总是由给它上锁的线程解锁;互斥锁要么被锁住,要么被解开。5)要注意理解互斥锁和条件变量的特点,从而明白它们的使用场合,以及常规使用方法。6)本实验采用的互斥锁和条件变量都是全局变量,静态分配,没有采用动态分配方法。函数参考手册:函数参考手册:静态分配一个互斥

30、锁:static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;注意:注意:如果互斥锁是动态分配的(例如通过调用 malloc),或者分配在共享内存区中,那么则必须在运行时刻通过调用 pthread_mutex_init 函数初始化它。给一个互斥锁上锁:#include int pthread_mutex_lock(pthread_mutex_t*mptr);int pthread_mutex_trylock(pthread_mutex_t*mptr);返回:成功时为 0,出错时为正的 Exxx 值 注意:注意:如果尝试给一个已经由另外某个线程锁

31、住的互斥锁上锁,那么 pthread_mutex_lock 将阻塞到该互斥锁解锁为止。pthread_mutex_trylock 是对应的非阻塞函数,如果该互斥锁已经锁住,它就返回一个 EBUSY 错误。给一个互斥锁解锁:#include int pthread_mutex_unlock(pthread_mutex_t*mptr);返回值:成功时为 0,出错时为正的 Exxx 值 定一条件变量:pthread_cond_t cond;注意:注意:互斥锁用于上锁,条件变量则用于等待。因而常常可用条件变量实现线程间的同步 等待某个条件:#include int pthread_cond_wait(

32、pthread_cond_t*cptr,pthread_mutex_t*mpthr);int pthread_cond_signal(pthread_cond_t*cptr);返回值:成功时为 0,出错时为正的 Exxx 值 注意:注意:每个条件变量总是有一个互斥锁与之关联。在程序中调用 pthread condwait 等待某个条件为真时,同时指定其条件变量的地址和所关联的互斥锁的地址。唤醒等待在相应条件变量上的一个线程:#include int pthread_cond_signal(pthread_cond_t*cptr);返回:成功时为 0,出错时为正的 Exxx 值 注意:注意:pt

33、hread_cond_signal 只唤醒等待在相应条件变量上的一个线程,某些情况下一个线程认定有多个其他线程应被唤醒,这时它可以调用 pthread_cond_broadcase 唤醒组赛在相应条件变量上的所有线程。实验样例:实验样例:例 1:用互斥锁实现生产者消费者模型。#include#include#define MAXNITEMS 1000000#define MAXNTHREADS 100 int nitems;/shared 结构中的变量是各个线程间共享的,将它们放在一个结构中是为了强调这些变量只能在线程拥有互斥锁时访问。struct pthread_mutex_t mutex;

34、/互斥锁 int buffMAXNITEMS;int nput;/时 buff 数组中下一个存放元素的小标 int nval;/是下一次存放的值 shared=PTHREAD_MUTEX_INITIALIZER;void*produce(void*);void*consume(void*);int main(int argc,char*argv)int i,nthreads,countMAXNTHREADS;pthread_t tid_produceMAXNTHREADS,tid_consume;if(argc!=3)perror(usage:pc items thread);exit(1);

35、if(atoi(argv1)MAXNITEMS)/第一个命令行参数指定生产者存放的项数 nitems=atoi(argv1);else nitems=MAXNITEMS;if(atoi(argv2)MAXNTHREADS)/第二个命令行参数指定待创建的生产者线程数 nthreads=atoi(argv2);else nthreads=MAXNTHREADS;/set_concurrency(nthreads);/告诉线程系统希望并发运行几个线程 for(i=0;inthreads;i+)counti=0;/创建生产者线程,每个线程执行 produce,在 tid_produce 数组中保存每个

36、线程的线程ID。传递给每个生产者线程的参数是指向 count 数组中某个元素的指针。首先把该计数器初始化成为 0,每个线程然后在每次往缓冲区 buff 中存放一项时给这个计数器加 1。当一切生产完毕时,输出这个计数器数组各元素的值,以查看每个生产者线程分别存放了多少项。pthread_create(&tid_producei,NULL,produce,&counti);for(i=0;i=nitems)/测试是否生产完毕 pthread_mutex_unlock(&shared.mutex);pthread_exit(NULL);shared.buffshared.nput=shared.nv

37、al;shared.nput+;shared.nval+;pthread_mutex_unlock(&shared.mutex);*(int*)arg)+=1;void*consume(void*arg)int i;for(i=0;initems;i+)if(shared.buffi!=i)printf(buff%d=%dn,i,shared.buffi);pthread_exit(NULL);例例 2:用互斥锁实现生产者消费者模型。#include#include#define MAXNITEMS 1000000 /*buffer MAX capacity*/#define MAXNTHRE

38、ADS 100 /*MAX thread number*/int nitems;struct pthread_mutex_t mutex;/*mutex varieable*/int buffMAXNITEMS;int nput;int nval;shared=PTHREAD_MUTEX_INITIALIZER;void*produce(void*);void consume_wait(int i);void*consume(void*);int main(int argc,char*argv)int i,nthreads,countMAXNTHREADS;pthread_t tid_prod

39、uceMAXNTHREADS,tid_consume;if(argc!=3)perror(usage:./pc3 );exit(1);if(atoi(argv1)MAXNITEMS)nitems=atoi(argv1);else nitems=MAXNITEMS;if(atoi(argv2)MAXNTHREADS)nthreads=atoi(argv2);else nthreads=MAXNTHREADS;for(i=0;inthreads;i+)counti=0;pthread_create(&tid_producei,NULL,produce,&counti);pthread_create

40、(&tid_consume,NULL,consume,NULL);for(i=0;i=nitems)pthread_mutex_unlock(&shared.mutex);pthread_exit(NULL);shared.buffshared.nput=shared.nval;shared.nput+;shared.nval+;pthread_mutex_unlock(&shared.mutex);*(int*)arg)+=1;void consume_wait(int i)for(;)pthread_mutex_lock(&shared.mutex);if(i shared.nput)pt

41、hread_mutex_unlock(&shared.mutex);return;pthread_mutex_unlock(&shared.mutex);void*consume(void*arg)int i;for(i=0;initems;i+)consume_wait(i);if(shared.buffi!=i)printf(buff%d=%dn,i,shared.buffi);pthread_exit(NULL);例例 3:用互斥锁、条件变量实现的一种生产者消费者模型。#include#include#define MAXNITEMS 1000000#define MAXNTHREADS

42、 100 int nitems;struct pthread_mutex_t mutex;int buffMAXNITEMS;int nput;int nval;shared=PTHREAD_MUTEX_INITIALIZER;struct pthread_mutex_t mutex;pthread_cond_t cond;int nready;nready=PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER;void*produce(void*);void*consume(void*);int main(int argc,char*argv)

43、int i,nthreads,countMAXNTHREADS;pthread_t tid_produceMAXNTHREADS,tid_consume;if(argc!=3)perror(usage:pc items thread);exit(1);if(atoi(argv1)MAXNITEMS)nitems=atoi(argv1);else nitems=MAXNITEMS;if(atoi(argv2)MAXNTHREADS)nthreads=atoi(argv2);else nthreads=MAXNTHREADS;/set_concurrency(nthreads);for(i=0;i

44、nthreads;i+)counti=0;pthread_create(&tid_producei,NULL,produce,&counti);pthread_create(&tid_consume,NULL,consume,NULL);for(i=0;i=nitems)pthread_mutex_unlock(&shared.mutex);pthread_exit(NULL);shared.buffshared.nput=shared.nval;shared.nput+;shared.nval+;pthread_mutex_unlock(&shared.mutex);pthread_mute

45、x_lock(&nready.mutex);if(nready.nready=0)pthread_cond_signal(&nready.cond);nready.nready+;pthread_mutex_unlock(&nready.mutex);*(int*)arg)+=1;void*consume(void*arg)int i;for(i=0;initems;i+)pthread_mutex_lock(&nready.mutex);if(nready.nready=0)pthread_cond_wait(&nready.cond,&nready.mutex);nready.nready

46、-;pthread_mutex_unlock(&nready.mutex);if(shared.buffi!=i)printf(buff%d=%dn,i,shared.buffi);pthread_exit(NULL);上机练习:上机练习:Ex1:阅读并运行例 1,观察执行结果,说出例 1 描述的是怎样的一种情形?Ex2:对于例 1,在生产者函数 void*produce(void*arg)代码中,最后一行语句是*(int*)arg)+=1;。下面的问题是,为什么将*(int*)arg)+=1;这条语句放在互斥锁外,为什么能够这样?Ex3:阅读并运行例 2,观察执行结果,说出例 2 与例 1

47、有什么不同,即例 2 描述的是怎样的一种情形?Ex4:对于例 2,请你说出 void consume_wait(int i)函数的作用?Ex5:阅读并运行例 3,观察执行结果,说出例 3 描述的是怎样一种情形?Ex6:对于例 3,它用到了互斥锁和条件变量,比较例 2 和例 3 的不同之处。4.9 实验六 用 Posix 信号量实现生产者4.9 实验六 用 Posix 信号量实现生产者/消费者 消费者 实验题目:实验题目:用 Posix 信号量实现生产者消费者模型 实验目的:实验目的:1)掌握生产者消费者模型 2)掌握 Posix 信号量常用用法 实验分析:实验分析:本实验虽然也是实现生产者消费

48、者模型,但要求用 Posix 信号量机制实现同步互斥。我们知道,信号量、互斥锁、条件变量它们都用于实现同步,但是这三者之间有一定的差异。1.互斥锁必须总是由给它上锁的线程解锁,信号量的挂出却不必由执行过它的等待操作的同一线程执行。2.互斥锁要么被锁住,要么被解开(二值状态、类似于二值信号量)。3.既然信号量有一个与之关联的状态(它的计数值),因而信号量的挂起操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。所以,尽管信号量的意图在于进程间同步,互斥锁和条件变量的意图则在于线程间的同步,但是信号量也可用于线程间,互斥锁和条件变量也可用于进程间。Po

49、six 提供两类信号量:有名(named)信号量和基于内存(memory-based)的信号量,后者也称为无名信号量。有名信号量能够在不同进程间共享,基于内存的信号量则必须是在创建时指定成是否在进程间共享。这两类信号量的持续性也有差别,有名信号量有随内核的持续性,基于内存的信号量则具有随进程的持续性。当不需要与有名信号量关联的名字时,可以使用基于内存的信号量。彼此无亲缘关系的不同进程需使用信号量时,通常使用有名信号量,其名字就是各个进程标识信号量的手段。基于内存的信号量具有随进程的持续性,然而它们真正的持续性却取决于信号量存放在其中的内存区的类型。只要含有某个基于内存信号量的内存区保持有效,该

50、信号量就一直存在。本实验用基于内存的信号量实现生产者消费者模型。但在库函数参考中,我们还是一并给出有名信号量的库函数参考。函数参考手册:函数参考手册:创建一个新的有名信号量或者打开一个已经存在的有名信号量:#include sem_t*sem_open(const char*name,int oflag,)返回:成功时为指向信号量的指针,出错时为 SEM_FAILED 注意:注意:sem_open的返回值是指向某个sem_t数据类型的指针。该指针随后用作sem_close、sem_wait、sem_trywait、sem_post 以及 sem_getValue 的参数。关闭信号量:#incl

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁