操作系统 实验 五 线程间的互斥与同步

操作系统 实验 五 线程间的互斥与同步
操作系统 实验 五 线程间的互斥与同步

实验五线程间的互斥与同步

实验学时:2学时

实验类型:验证、设计型

一、实验目的

理解POSIX线程(Pthread)互斥锁和POSIX信号量机制,学习它们的使用方法;编写程序,实现多个POSIX线程的同步控制。

二,实验内容

创建4个POSIX线程。其中2个线程(A和B)分别从2个数据文件(data1.txt和data2.txt)读取10个整数. 线程A和B把从文件中读取的逐一整数放入一个缓冲池. 缓冲池由n个缓冲区构成(n=5,并可以方便地调整为其他值),每个缓冲区可以存放一个整数。另外2个线程,C和D,各从缓冲池读取10数据。线程C、D每读出2个数据,分别求出它们的和或乘积,并打印输出。

提示:在创建4个线程当中,A和B是生产者,负责从文件读取数据到公共的缓冲区,C和D是消费者,从缓冲区读取数据然后作不同的计算(加和乘运算)。使用互斥锁和信号量控制这些线程的同步。不限制线程C和D从缓冲区得到的数据来自哪个文件。

在开始设计和实现之前,务必认真阅读课本6.8.4节和第6章后面的编程项目——生产者-消费者问题。

三,实验要求

按照要求编写程序,放在相应的目录中,编译成功后执行,并按照要求分析执行结果,并写出实验报告。

四,实验设计

1,功能设计

根据实验要求,主程序需要创建四个线程,两个线程负责从文件读取数据到缓冲区,两个线程负责将缓冲区的数据做数学运算。由于同一个进程中的各个线程共享资源,可以用一个二维数组的全局变量作为公共缓冲区,同时还需要一个整形全局变量size用来做数组的索引。读线程的运行函数打开不同的文件并从中读取数据到二维数组中,每次写入数组后size加一。运算线程从二维数组中读数并做运算,每次读数之前size减一。本题的关键在于如何使用信号量保证进程的同步与互斥。在运算线程从缓冲区读取之前缓冲区里必须有数,即任意时刻运算操作的执行次数必须小于等于读取操作的执行次数。同时应该保证两个读线程和两个运算线程两两互斥。由于以上分析,使用了四个信号量sem1,sem2,sem3和sem4。sem1保证线程1和线程2互斥,sem2保证线程3和线程4互斥,sem3保证线程3和线程4互斥,sem4保证线程4和线程1互斥。即这四个信号量使四个线程循环进行,从而保证了运行结果的正确性。

源代码及注释:

#include

#include

#include

#define NUM 200

int stack[NUM][2]; //公共缓冲区

int size=0; //初始化数组索引

sem_t sem1,sem2,sem3,sem4; //定义四个信号量

void read1(void){ //线程运行函数,负责从文件读取数据FILE *fp=fopen("data0.txt","r"); //以只读方式打开文件data1

while(!feof(fp)){

sem_wait(&sem1); //减少信号量sem1

if(!fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]))return;

size++; //读两个数到公共缓冲区

sem_post(&sem2); //增加信号量sem2

}

fclose(fp); //关闭文件

}

void read2(void){ //线程运行函数,负责从文件读取数据FILE *fp=fopen("data1.txt","r"); //以只读方式打开文件data2

while(!feof(fp)){

sem_wait(&sem2); //减少信号量sem2

if(!fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]))return;

size++; //读两个数到公共缓冲区

sem_post(&sem3); //增加信号量sem3

}

fclose(fp);

}

void plus1(void){ //线程运行函数,负责加运算

while(1){

sem_wait(&sem3); //减少信号量sem3

if(size==0){return;}

size--;

printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],stack[size][0]+stack[size][1] ); //从公共缓冲区取数并进行加运算

sem_post(&sem4); //增加信号量sem3

}

}

void multi2(void){ //线程运行函数,负责乘运算

while(1){

sem_wait(&sem4); //减少信号量sem4

if(size==0){return;}

size--;

printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],stack[size][0]*stack[size ][1]); //从公共缓冲区取数并进行乘运算

sem_post(&sem1); //增加信号量sem3

}

}

int main(void){

pthread_t t1,t2,t3,t4; //定义线程标识符

sem_init(&sem1,0,1); //初始化信号量

sem_init(&sem2,0,0);

sem_init(&sem3,0,0);

sem_init(&sem4,0,0);

pthread_create(&t1,NULL,(void *)read1,NULL); //创建线程,绑定线程运行函数pthread_create(&t2,NULL,(void *)read2,NULL);

pthread_create(&t3,NULL,(void *)plus1,NULL);

pthread_create(&t4,NULL,(void *)multi2,NULL);

pthread_join(t1,NULL); //等待线程运行结束

}

2,数据结构

信号量(semaphore):数据类型为结构 sem_t,本质上是一个长整型的数。

公共缓冲区(stack):采用2维数组的方式实现(stack[NUM][2])。数组中的两列分别存储两个文件中的数据。该2维数组还有一个索引:size,指向2维数组的顶部。

五,实验测试结果及分析

实验截图:

如图所示,加线程和乘线程从缓冲区读取数据,进行运算,并将运算结果输出。

六,收获及体会

在本次实验中,我学习了UNIX 类操作系统信号量机制,掌握了编写Linux 环境下利用信号量实现进程控制的方法及相关系统调用的使用方法。在编译的时候也遇到了一些问题,在读取时data0和data1时出现过读取的错误,在使用信号量对线程控制时出现了一些错误,运算总是出现顺序问题。通过测试与调试,我找出了错误原因,并修改了程序中的信号量设置方式,严格控制各个线程的执行顺序,终于解决了问题。在这个过程中我加深了对于信号量操作的理解与认识。

4:一个经典的多线程同步问题汇总

一个经典的多线程同步问题 程序描述: 主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。 要求: 1.子线程输出的线程序号不能重复。 2.全局变量的输出必须递增。 下面画了个简单的示意图: 分析下这个问题的考察点,主要考察点有二个: 1.主线程创建子线程并传入一个指向变量地址的指针作参数,由于线程启动须要花费一定的时间,所以在子线程根据这个指针访问并保存数据前,主线程应等待子线程保存完毕后才能改动该参数并启动下一个线程。这涉及到主线程与子线程之间的同步。 2.子线程之间会互斥的改动和输出全局变量。要求全局变量的输出必须递增。这涉及到各子线程间的互斥。 下面列出这个程序的基本框架,可以在此代码基础上进行修改和验证。 //经典线程同步互斥问题 #include #include #include long g_nNum; //全局资源 unsigned int__stdcall Fun(void *pPM); //线程函数 const int THREAD_NUM = 10; //子线程个数 int main() { g_nNum = 0;

HANDLE handle[THREAD_NUM]; int i = 0; while (i < THREAD_NUM) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); i++;//等子线程接收到参数时主线程可能改变了这个i的值} //保证子线程已全部运行结束 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; } unsigned int__stdcall Fun(void *pPM) { //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来int nThreadNum = *(int *)pPM; //子线程获取参数 Sleep(50);//some work should to do g_nNum++; //处理全局资源 Sleep(0);//some work should to do printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum); return 0; } 运行结果:

进程同步机制与互斥-生产者消费者问题

学习中心: 专业: 年级:年春/秋季 学号: 学生: 题目:进程同步与互斥生产者-消费者问题 1.谈谈你对本课程学习过程中的心得体会与建议? 转眼间,学习了一个学期的计算机操作系统课程即将结束。在这个学期中,通过老师的悉心教导,让我深切地体会到了计算机操作系统的一些原理和具体操作过程。在学习操作系统之前,我只是很肤浅地认为操作系统只是单纯地讲一些关于计算机方面的操作应用,并不了解其中的具体操作过程 1.1设计思路 在这次设计中定义的多个缓冲区不是环形循环的,并且不需要按序访问。其中生产者可以把产品放到某一个空缓冲区中,消费者只能消费被指定生产者生产的产品。本设计在测试用例文件中指定了所有生产和消费的需求,并规定当共享缓冲区的数据满足了所有有关它的消费需求后,此共享才可以作为空闲空间允许新的生产者使用。

本设计在为生产者分配缓冲区时各生产者之间必须互斥,此后各个生产者的具体生产活动可以并发。而消费者之间只有在对同一个产品进行消费时才需要互斥,它们在消费过程结束时需要判断该消费者对象是否已经消费完毕并释放缓冲区的空间。 1.2程序流程图 1.3基本内容 在设计程序时主要有三个主体部分、三个辅助函数和一个数据结构。 其中主体部分为一个主函数main(),用于初始化缓冲区和各个同步对象,并完成线程信息的读入,最后根据该组的线程记录启动模拟线程,并等待所有线程的运 Y

行结束后退出程序; 生产者函数Produce()和消费者函数Consume(),生产者和消费者函数运行于线程中完成对缓冲区的读、写动作,根据此处生产消费的模型的特点,生产者和消费者之间通过使用同步对象实现了生产和消费的同步与互斥,是本实验的核心所在。 另外三个辅助性函数被生产者和消费者函数调用,是上述生产和消费函数中对缓冲区进行的一系列处理。 3)在实现本程序的消费生产模型时,具体的通过如下同步对象实现互斥: ①设一个互斥量h_mutex,以实现生产者在查询和保留缓冲区内的下一个位置时进行互斥。 ②每一个生产者用一个信号量与其消费者同步,通过设置h_Semaphore[MAX_THREAD_NUM]信号量 ③数组实现,该组信号量用于相应的产品已产生。同时用一个表示空缓冲区

用信号量实现线程同步与互斥

用信号量实现线程同步与互斥 一、相关Win32 API函数 1、创建线程 HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 函数作用:在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄。 各参数含义: ?lpThreadAttributes:指向一个SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为NULL; ?dwStackSize:指定了线程的堆栈深度,一般都设置为0; ?lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。 一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数 名;函数名称没有限制,但是必须以下列形式声明:DWORD WINAPI ThreadProc (PVOID pParam) ; ?lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数; ?dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产 生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用; ?lpThreadId:该参数返回所创建线程的ID。 如果创建成功则返回线程的句柄,否则返回NULL。 例如: for (int i=0;i

第3章 进程同步与通信 练习题答案

第3章进程同步与通信练习题 (一)单项选择题 1.临界区是指( )。 A.并发进程中用于实现进程互斥的程序段 B.并发进程中用于实现进程同步的程序段 C.并发进程中用户实现进程通信的程序段 D.并发进程中与共享变量有关的程序段 2.相关临界区是指( )。 A.一个独占资源 B.并发进程中与共享变量有关的程序段 c.一个共享资源 D.并发进程中涉及相同变量的那些程序段 3.管理若干进程共享某一资源的相关临界区应满足三个要求,其中( )不考虑。 A一个进程可以抢占己分配给另一进程的资源 B.任何进程不应该无限地逗留在它的临界区中c.一次最多让一个进程在临界区执行 D.不能强迫一个进程无限地等待进入它的临界区4、( )是只能由P和v操作所改变的整型变量。 A共享变量 B.锁 c整型信号量 D.记录型信号量 5.对于整型信号量,在执行一次P操作时,信号量的值应( )。 A.不变 B.加1 C减1 D.减指定数值 6.在执行v操作时,当信号量的值( )时,应释放一个等待该信号量的进程。 A>0 B.<0 c.>=0 D.<=0 7.Pv操作必须在屏蔽中断下执行,这种不可变中断的过程称为( )。 A初始化程序 B.原语 c.子程序 D控制模块 8.进程间的互斥与同步分别表示了各进程间的( )。 A.竞争与协作 B.相互独立与相互制约 c.不同状态 D.动态性与并发性 9并发进程在访问共享资源时的基本关系为( )。 A.相互独立与有交往的 B.互斥与同步 c并行执行与资源共享 D信息传递与信息缓冲 10.在进程通信中,( )常用信件交换信息。 A.低级通信 B.高级通信 c.消息通信 D.管道通信 11.在间接通信时,用send(N,M)原语发送信件,其中N表示( )。 A.发送信件的进程名 B.接收信件的进程名 C信箱名 D.信件内容 12.下列对线程的描述中,( )是错误的。 A不同的线程可执行相同的程序 B.线程是资源分配单位 c.线程是调度和执行单位 D.同一 进程中的线程可共享该进程的主存空间 13.实现进程互斥时,用( )对应,对同一个信号量调用Pv操作实现互斥。 A.一个信号量与一个临界区 B.一个信号量与—个相关临界区 c.一个信号量与一组相关临界 区 D一个信号量与一个消息 14.实现进程同步时,每一个消息与一个信号量对应,进程( )可把不同的消息发送出去。 A.在同一信号量上调用P操作 B在不同信号量上调用P操作 c.在同一信号量上调用v操作D.在不同信号量上调用v操作 (二)填空题 1.目前使用的计算机的基本特点是处理器______执行指令。 2.进程的______是指进程在顺序处理器上的执行是按顺序进行的。 3.当一个进程独占处理器顺序执行时,具有______和______两个特性。 4.进程的封闭性是指进程的执行结果只取决于______,不受外界影响。 5 进程的可再现性是指当进程再次重复执行时,必定获得______的结果。 6.一个进程的工作在没有全部完成之前,另一个进程就可以开始工作,则称这些进程为______.

同步和互斥实验-吃水果问题、消费者问题

实验3 报告 源程序1 /** 作者:wwj 时间:2012/4/12 功能:实现吃水果问题 **题目内容:桌子有一只盘子,只允许放一个水果,父亲专向盘子放苹果,母亲专向盘子放桔子儿子专等吃盘子的桔子,女儿专等吃盘子的苹果。只要盘子为空,父亲或母亲就可以向盘子放水果,仅当盘子有自己需要的水果时,儿子和女儿可从盘子取出。请给出四个人之间的同步关系,并用pv操作实现四个人的正确活动的问题。** **题目分析:父亲和女儿是相互制约的,父亲进程执行完即往盘中放入苹果后,女儿进程才能执行即吃苹果,是同步关系; 母亲和儿子是相互制约的,母亲进程执行完即往盘中放入桔子,儿子进程才能执行即吃桔子,也是同步关系 而父亲和母亲这两个进程不能同时进行,是互斥关系;** **/ #include #include using namespace std; //声明句柄 HANDLE EmptyPlate; HANDLE Apple; HANDLE orange; HANDLE fatherThread; HANDLE motherThread; HANDLE sonThread; HANDLE daughterThread; //线程函数声明 DWORD WINAPI father(LPVOID IpParameter); DWORD WINAPI mother(LPVOID IpParameter); DWORD WINAPI daughter(LPVOID IpParameter); DWORD WINAPI son(LPVOID IpParameter); int main() { //创建信号量 EmptyPlate = CreateSemaphore(NULL,1,1,NULL); //盘子 Apple = CreateSemaphore(NULL,0,1,NULL); //苹果

操作系统 实验 五 线程间的互斥与同步

实验五线程间的互斥与同步 实验学时:2学时 实验类型:验证、设计型 一、实验目的 理解POSIX线程(Pthread)互斥锁和POSIX信号量机制,学习它们的使用方法;编写程序,实现多个POSIX线程的同步控制。 二,实验内容 创建4个POSIX线程。其中2个线程(A和B)分别从2个数据文件(data1.txt和data2.txt)读取10个整数. 线程A和B把从文件中读取的逐一整数放入一个缓冲池. 缓冲池由n个缓冲区构成(n=5,并可以方便地调整为其他值),每个缓冲区可以存放一个整数。另外2个线程,C和D,各从缓冲池读取10数据。线程C、D每读出2个数据,分别求出它们的和或乘积,并打印输出。 提示:在创建4个线程当中,A和B是生产者,负责从文件读取数据到公共的缓冲区,C和D是消费者,从缓冲区读取数据然后作不同的计算(加和乘运算)。使用互斥锁和信号量控制这些线程的同步。不限制线程C和D从缓冲区得到的数据来自哪个文件。 在开始设计和实现之前,务必认真阅读课本6.8.4节和第6章后面的编程项目——生产者-消费者问题。

三,实验要求 按照要求编写程序,放在相应的目录中,编译成功后执行,并按照要求分析执行结果,并写出实验报告。 四,实验设计 1,功能设计 根据实验要求,主程序需要创建四个线程,两个线程负责从文件读取数据到缓冲区,两个线程负责将缓冲区的数据做数学运算。由于同一个进程中的各个线程共享资源,可以用一个二维数组的全局变量作为公共缓冲区,同时还需要一个整形全局变量size用来做数组的索引。读线程的运行函数打开不同的文件并从中读取数据到二维数组中,每次写入数组后size加一。运算线程从二维数组中读数并做运算,每次读数之前size减一。本题的关键在于如何使用信号量保证进程的同步与互斥。在运算线程从缓冲区读取之前缓冲区里必须有数,即任意时刻运算操作的执行次数必须小于等于读取操作的执行次数。同时应该保证两个读线程和两个运算线程两两互斥。由于以上分析,使用了四个信号量sem1,sem2,sem3和sem4。sem1保证线程1和线程2互斥,sem2保证线程3和线程4互斥,sem3保证线程3和线程4互斥,sem4保证线程4和线程1互斥。即这四个信号量使四个线程循环进行,从而保证了运行结果的正确性。 源代码及注释: #include #include #include #define NUM 200

操作系统实验-进程同步与互斥

实验四:进程的管道通信 实验题目 进程的管道通信 实验目的 加深对进程概念的理解,明确进程和程序的区别。学习进程创建的过程,进一步认识进程并发执行的实质。分析进程争用资源的现象,学习解决进程互斥的方法。学习解决进程同步的方法。掌握Linux系统中进程间通过管道通信的具体实现 实验内容 使用系统调用pipe()建立一条管道,系统调用fork()分别创建两个子进程,它们分别向管道写一句话,如: Child process1 is sending a message! Child process2 is sending a message! 父进程分别从管道读出来自两个子进程的信息,显示在屏幕上。 当然,仅仅通过屏幕上输出这两句话还不能说明实现了进程的管道通信,为了能够更好的证明和显示出进程的同步互斥和通信,在其中要加入必要的跟踪条件,如一定的输出语句等,来反映程序的并发执行情况 实验要求 这是一个设计型实验,要求自行、独立编制程序。两个子进程要并发执行。实现管道的互斥使用。当一个子进程正在对管道进行写操

作时,另一个欲写入管道的子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。实现父子进程的同步,当父进程试图从一空管道中读取数据时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。 为了清楚的反应进程的同步,在子进程完成相应的操作后,调用sleep()函数睡眠一段时间(程序中定为3s)。父进程先执行wait()函数,当有子进程执行完毕后,会得到子进程的返回结果并清理子进程。若子进程没执行完,父进程一直执行wait()进行监听,知道有一个子进程执行完成为僵尸进程。 程序中用到的系统调用 因为程序时在linux系统上进行编写的,所以其中要利用到相关的linux提供的系统调用。 所用到的系统调用包含在如下头文件中。 #include #include #include #include #include #include fork() 用于创一个子进程。 格式:int fork();

四种进程或线程同步互斥的控制方法

四种进程或线程同步互斥的控制方法 1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 3、信号量:为控制一个具有有限数量用户资源而设计。 4、事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 一临界区 临界区的使用在线程同步中应该算是比较简单,说它简单还是说它同后面讲到的其它方法相比更容易理解。举个简单的例子:比如说有一个全局变量(公共资源)两个线程都会对它进行写操作和读操作,如果我们在这里不加以控制,会产生意想不到的结果。假设线程A 正在把全局变量加1然后打印在屏幕上,但是这时切换到线程B,线程B又把全局变量加1然后又切换到线程A,这时候线程A打印的结果就不是程序想要的结果,也就产生了错误。解决的办法就是设置一个区域,让线程A在操纵全局变量的时候进行加锁,线程B如果想操纵这个全局变量就要等待线程A释放这个锁,这个也就是临界区的概念。 二互斥体 windows api中提供了一个互斥体,功能上要比临界区强大。也许你要问,这个东东和临界区有什么区别,为什么强大?它们有以下几点不一致: 1.critical section是局部对象,而mutex是核心对象。因此像waitforsingleobject是不可以等待临界区的。 2.critical section是快速高效的,而mutex同其相比要慢很多 3.critical section使用围是单一进程中的各个线程,而mutex由于可以有一个名字,因此它是可以应用于不同的进程,当然也可以应用于同一个进程中的不同线程。 4.critical section 无法检测到是否被某一个线程释放,而mutex在某一个线程结束之后会产生一个abandoned的信息。同时mutex只能被拥有它的线程释放。下面举两个应用mutex 的例子,一个是程序只能运行一个实例,也就是说同一个程序如果已经运行了,就不能再运行了;另一个是关于非常经典的哲学家吃饭问题的例子。 三事件 事件对象的特点是它可以应用在重叠I/O(overlapped I/0)上,比如说socket编程中有两种模型,一种是重叠I/0,一种是完成端口都是可以使用事件同步。它也是核心对象,因此可以被waitforsingleobje这些函数等待;事件可以有名字,因此可以被其他进程开启。 四信号量 semaphore的概念理解起来可能要比mutex还难,我先简单说一下创建信号量的函数,因为我在开始使用的时候没有很快弄清楚,可能现在还有理解不对的地方,如果有错误还是请大侠多多指教。 CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD LONG lInitialCount, // initial count LONG lMaximumCount, // maximum count LPCTSTR lpName // object name )

同步与互斥

习题解答: ?何谓与时间有关的错误? 举例说明之。 答:并发进程的执行实际上是进程活动的某种交叉,某些交叉次序可能得到错误结果。由于具体交叉的形成与进程的推进速度有关,而速度是时间的函数,因而将这种错误称为与时间有关的错误。 例如,两个并发进程的程序如下: int n=0; main( ){ 创建进程A; 创建进程B; }; A( ){ while(1){ n++; } }; B( ){ while(1){ 睡眠一段时间;printf(“%d”,n); n=0; } }; 假设进程A被部署在公园入口的终端上,用来记录一段时间内进入公园的人数,进程B被部署在公园的控制中心,用来输出一段时间内进入公园的总人数。进程A和进程B共享全局变量n,n表示记录下的人数。如果在进程B执行完打印语句后被进程A打断,进程A执行了若干次变量自增语句,之后进程B接着执行清0语句,那么进程A对n的累加丢失了,相当于进程B被打断的这段时间内进入公园的人没有被记录下来。发生与时间有关的错误。 ?有人说,假设两个进程之间没有共享内存,则二者之间没有公共变量,这种说法准确吗? 说明原因。 答:如果只从用户空间考虑,这种说法是正确的。但从操作系统的角度来说并不准确。两个没有公共内存的用户进程可能同时(宏观)进入操作系统,并访问操作系统空间中的公共变

量。 ?何谓忙式等待? 是否还有其它方式的等待? 比较它们之间的联系和差别。 答:不进入等待状态的等待称为忙式等待。另一种等待方式是阻塞式等待,进程得不到共享资源时将进入阻塞状态,让出CPU给其他进程使用。忙等待和阻塞式等待的相同之处在于进程都不具备继续向前推进的条件,不同之处在于处于忙等待的进程不主动放弃CPU,尽管CPU 可能被剥夺,因而是低效的;而处于阻塞状态的进程主动放弃CPU,因而是高效的。 ?下列进程互斥方法哪些存在忙式等待问题? (1)软件: 面包店算法(2) 硬件: TS指令(3) 关中断指令 答:(1)、(2)存在忙等待问题。 ?为何开关中断进程互斥方法仅在单CPU系统中是有效的? 答:关中断方法不适用于多CPU系统,因为关中断只能保证CPU不由一个进程切换到另外一个进程,从而防止多个进程并发地进入公共临界区域。但即使关中断后,不同进程仍可以在不同CPU上并行执行关于同一组共享变量的临界区代码. ?在多处理机系统中,软件互斥方法是否有效?为什么? 答:依然有效。多处理机并行与单处理并发之间的差别在于程序交叉的粒度,单处理机机环境中进程交叉发生在指令之间,多处理机环境中进程交叉发生在指令周期之间。由于纯软件互斥算法并不依赖特殊的硬件指令(如test_and_set),指令之间的交叉与指令周期之间的交叉结果相同。 ?试分析临界区域的大小与系统并发性之间的关系。 答:关于同一组变量的临界区域是不能并发执行的代码,临界区越大,并发性越差,因而编写并发程序应尽量缩小临界区域范围。 ?设CR1是关于一组共享变量SV1的临界区域,CR2是关于另外一组共享变量SV2的临

进程同步与互斥汇总

进程同步与互斥

进程的PV操作 在操作系统中,P、V操作是进程管理中的难点。这是1968年荷兰人Dijkstra 给出的一种解决并发进程间互斥和同步关系的通用方法。 1. P、V操作的意义 定义了信号量及其上的P操作和V操作,来实现并发进程间的同步和互斥,甚至可以用来管理资源的分配。P、V操作因交换的信息量少,属于进程的低级通信。 2. 什么是信号量? 信号量(semaphore)是由一个值和一个指针构成的数据结构。值为整型变

量,表示信息量的值;指针指向进程控制块(PCB)队列的队头,表示等待该信号量的下一个进程。如下图所示。 信号量的一般结构及PCB队列 信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的初值不能为负,且其值只能由P、V操作来改变。 3. P、V操作的含义 P、V操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量S进行操作,具体定义如下: P(S): ①将信号量S的值减1,即S=S-1; ②如果S≥0,则该进程继续执行;否则该进程状态置为阻塞状态,进程PCB 排入信号量PCB队列末尾,放弃CPU,等待V操作的执行。 V(S): ①将信号量S的值加1,即S=S+1; ②如果S≤0,释放信号量队列中第一个PCB所对应的进程,将进程状态由阻塞态改为就绪态。执行V操作的进程继续执行。 一般来说,信号量S≥0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S≤0,表示有某些进程正在等待该资源,因此要唤醒一个阻塞状态的进程,使之成为就绪状态。 4. 利用信号量和P、V操作实现进程互斥 一般地,n个进程利用信号量和P、V操作实现进程互斥的一般模型如下: 进程P 1进程P 2 ……进程Pn …… …… …… P(S); P(S); P(S); 临界区;临界区;临界区; V(S); V(S); V(S); …… …… …… …… 其中S是互斥信号量,初值为1。 使用P、V操作实现进程互斥时应该注意的问题是: (1)每个程序中,用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查P、V操作的成对性。 (2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。 (3)互斥信号量的初值一般为1。 由于用于互斥的信号量sem与所有的并发进程有关,所以称之为公有信号量。公有信号量的值反映了公有资源的数量。只要把临界区置于P(sem)和V(sem)

进程(线程)的同步与互斥实验报告

操作系统实验报告 课程名称操作系统实验名称进程(线程)的同步与互斥成绩 学生姓名作业君专业软件工程班级、学号 同组者姓名无实验日期2020 一、实验题目:进程(线程)的同步与互斥 二、实验目的: 自行编制模拟程序,通过形象化的状态显示,加深理解进程的概念、进程之间的状态转换及其所带来的PCB内容、组织的变化,理解进程与其PCB间的一一对应关系。1.掌握基本的同步与互斥算法,理解生产者消费者模型。 2.学习使用Windows中基本的同步对象,掌握相关API的使用方法。 3.了解Windows中多线程的并发执行机制,实现进程的同步与互斥 三、实验内容与要求: 1.实验内容 以生产者/消费者模型为依据,在Windows 环境下创建一个控制台进程,在该进程中创建n个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。 2.实验要求 学习并理解生产者/消费者模型及其同步/互斥规则; 学习了解Windows同步对象及其特性; 熟悉实验环境,掌握相关API的使用方法; 设计程序,实现生产者/消费者进程(线程)的同步与互斥; 四、算法描述(含数据结构定义)或流程图 #include #include #include #include #include #include using namespace std;

#define MAX_THREAD_NUM 64 //最大线程数 #define INTE_PER_SEC 1000 //延迟时间的毫秒值 const int SIZE_OF_BUFFER = 10; //缓冲区长度 int ProductID = 0; //产品号 int ConsumeID = 0; //将被消耗的产品号 int in = 0; //产品进缓冲区时的缓冲区下标 int out = 0; //产品出缓冲区时的缓冲区下标 bool running = true; //判断程序能否继续执行的逻辑值 int g_buffer[SIZE_OF_BUFFER]; //缓冲区是个循环队列 HANDLE g_hMutex; //公有信号量,用于线程间的互斥HANDLE g_hFullSemaphore; //生产者的私有信号量,当缓冲区满时迫使生产者等待 HANDLE g_hEmptySemaphore; //消费者的私有信号量,当缓冲区空时迫使消费者等待 //定义一个结构体用于存储线程的信息 struct ThreadInfo { int serial; //线程号 char entity; //线程类别(生产者或消费者) double delay; //等待时间 double persist; //操作时间 }; //生产者 void Producer(void* p) { //定义变量用于存储当前线程的信息 DWORD m_delay; DWORD m_persist; int m_serial; //从参数中获得信息 m_serial = ((ThreadInfo*)(p))->serial; m_delay = (DWORD)(((ThreadInfo*)(p))->delay * INTE_PER_SEC); m_persist = (DWORD)(((ThreadInfo*)(p))->persist * INTE_PER_SEC); while (running) { //P操作 cout << "生产者线程 " << m_serial << " 请求生产." << endl; WaitForSingleObject(g_hEmptySemaphore, INFINITE);

进程之间的同步互斥与通信理发师问题操作系统课程设计

进程之间的同步互斥与通信理发师问题操作系统课程设计

操作系统课程设计

目录 1. 课程设计目的 (3) 2. 设计要求 (3) 3. 问题具体描述 (3) 4. 设计分析 (3) 5. 设计分工 (4) 6. 数据结构说明 (4) 7. 系统结构说明 (4) 8. 系统调用说明 (4) 9. 分工设计说明 (5) 10. 算法流程图 (5) 11. 分工代码 (6) 12. 整体代码 (7) 13. 程序运行 (10) 14. 总结 (11)

1.课程设计目的 1.内容围绕操作系统原理中最重要的基本概念和基本原理展开 2.巩固对原理知识的学习效果 3.加深对基本概念的理解 4.学习如何将基本原理和实际设计、应用有机结合 5.锻炼本专业的基本能力 2.设计要求 1:进程间通信、并发(同步/互斥)、文件读写2:内存管理、Dll、Windows消息机制、IO (尚未最终定型) 3.问题具体描述 1.完成N个生产者和M个消费者之间的并发控制,N、M不低于5,数据发送和接收缓冲区大小不小于10个。 2.某个生产者进程生产的消息供K个消费者进程消费。K《=M。某些消费进程消费多个生产者生产的消息。生产者和消费者之间的对应关系可以在程序开始有一个文件中读入,也可以动态

调整。 3.每个生产进程生产M个消息后结束运行。如果一个消费者进程没有对应的生产者进程在运行后,也结束运行。 4.设计分析 课程设计的主要目的是了解并且掌握进程之间的同步互斥,和进程之间的通信问题。结合课本上的生产者与消费者问题可以从这方面来实现一个多进程的小系统,并且解决多个进程之间的通信,并发等问题,以此来达到课程设计的目的。理发师问题是将顾客看做生产者,将理发师作为消费者。设置一定数量的椅子的数目来作为缓存区的大小。顾客来到的时候坐在椅子上,将自己作为“产品”,理发师理发的时候从椅子上叫走顾客,相当于消费“产品”,从而达到了课程设计要求的前一个要求。 顾客作为生产者,每到来一个就使计数器count增加1,以便让理发师理发(相当于消费)至最后一个顾客(相当于产品)。并且,第1个到来的顾客应负责唤醒理发师;如果不是第1个到达的顾客,则在有空椅子的情况下坐下等待,

实验1编程实现进程(线程)同步和互斥

实验1编程实现进程(线程)同步和互斥 一、实验目的 ①通过编写程序实现进程同步和互斥,使学生掌握有关进程(线程)同步与 互斥的原理,以及解决进程(线程)同步和互斥的算法,从而进一步巩固进程(线程)同步和互斥 ②等有关的内容。 ③了解Windows2000/XP中多线程的并发执行机制,线程间的同步和互斥。 ④学习使用Windows2000/XP中基本的同步对象,掌握相应的 ⑤API函数。 ⑥掌握进程和线程的概念,进程(线程)的控制原语或系统调用的使用。 ⑦掌握多道程序设计的基本理论、方法和技术,培养学生多道程序设计的能 力。 二、实验内容 在Windows XP、Windows 2000等操作系统下,使用的VC、VB、java或C 等编程语言,采用进程(线程)同步和互斥的技术编写程序实现生产者消费者问题或哲学家进餐问题或读者-写者问题或自己设计一个简单进程(线程)同步和互斥的实际问题。 三、实验要求 ①经调试后程序能够正常运行。 ②采用多进程或多线程方式运行,体现了进程(线程)同步和互斥的关系。 ③程序界面美观。 四、实验步骤、过程 让写者与读者、读者与读者之间互斥的访问同一数据集,在无写者进程到来时各读者可同时的访问数据集,在读者和写者同时等待时写者优先唤醒。设置两个全局变量readcount 和writecount来记录读者与写者的数目,设置了3个信号量。h_mutex1表示互斥对象对阻塞在read这一个过程实现互斥,h_mutex2实现全局变量readcount操作上的互斥,h_mutex3实现对全局变量writecount的互斥访问。设置了两个临界区,为了实现写者优先,用了临界区read。数据结构:(1)用了两个临界区(2)自定义结构ThreadInfo记录一条线程信息,多个线程对应一个ThreadInfo数组。(3)设置了互斥量h_mutex1,实现了互斥对象对阻

同步和互斥的区别_

同步和互斥的区别

信号量互斥量条件变量 同步和互斥 一、信号量:用于进程间通信(linux中用于线程) 独立于进程在两个进程中都要初始化,若一个已创建,则另一个获取可以执行多次v操作,然后再执行p操作 信号量的一些概念: 以下是信号灯(量)的一些概念: 信号灯与互斥锁和条件变量的主要不同在于”灯”的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于”等待”操作,即资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持灯亮状态。当然,这样的操作原语也意味着更多的开销。 信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。 二、互斥量:(用于在线程间的通信) 1、在一个进程中创建 三、信号量与互斥量的区别 1. 互斥量用于线程的互斥,信号量用于线程的同步。 这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源 以上区别是主要想记住的。 note:信号量可以用来实现互斥量的功能 2. 互斥量值只能为0/1,信号量值可以为非负整数。 也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。3、互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。 4、作用域 信号量: 进程间或线程间(linux仅线程间) 互斥锁: 线程间 5、上锁时 信号量: 只要信号量的value大于0,其他线程就可以sem_wait(p操作-1)成功,成功后信号量的value减一。若value值不大于0,则

多线程同步方法及比较

多线程同步方法及比较 多线程同步方法: 1.临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数 据访问。. 2.互斥量:为协调一起对一个共享资源的单独访问而设计的。. 3.信号量:为控制一个具备有限数量用户资源而设计。. 4.事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 临界区(Critical Section).. 确保在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。假如有多个线程试图同时访问临界区,那么在有一个线程进入后其他任何试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程能够继续抢占,并以此达到用原子方式操作共享资源的目的。 临界区包含两个操作原语: EnterCriticalSection()进入临界区 LeaveCriticalSection()离开临界区。 EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保和之匹配的LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。 MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是很简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。. ------------------------------------------------

Windows下进程同步与互斥

实验进程同步与互斥 一、实验目的 1.掌握基本的同步与互斥算法,理解生产者消费者模型。 2.学习使用Windows 2000/XP中基本的同步对象,掌握相关API的使用方法。 3.了解Windows 2000/XP中多线程的并发执行机制,实现进程的同步与互斥。 二、实验内容及要求 1.实验内容 以生产者/消费者模型为依据,在Windows 2000环境下创建一个控制台进程,在该进程中创建n个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。 2.实验要求 ●学习并理解生产者/消费者模型及其同步/互斥规则; ●学习了解Windows同步对象及其特性; ●熟悉实验环境,掌握相关API的使用方法; ●设计程序,实现生产者/消费者进程(线程)的同步与互斥; ●提交实验报告。 三、相关知识介绍 1.同步对象 同步对象是指Windows中用于实现同步与互斥的实体,包括信号量(Semaphore)、互斥量(Mutex)、临界区(Critical Section)和事件(Events)等。本实验中使用到信号量、互斥量和临界区三个同步对象。 同步对象的使用步骤: ●创建/初始化同步对象。 ●请求同步对象,进入临界区(互斥量上锁)。 ●释放同步对象(互斥量解锁)。 这些对象在一个线程中创建,在其他线程中都可以使用,实现同步与互斥。 2.相关API的功能及使用 我们利用Windows SDK提供的API编程实现实验题目要求,而VC中包含有Windows SDK的所有工具和定义。要使用这些API,需要包含堆这些函数进行说明的SDK头文件——最常见的是Windows.h(特殊的API调用还需要包含其他头文件)。 下面给出的是本实验使用到的API的功能和使用方法简单介绍。 (1) CreateThread ●功能——创建一个在调用进程的地址空间中执行的线程 ●格式 HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,

Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。 线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。 Hello World(线程创建、结束、等待) 创建线程 pthread_create 线程创建函数包含四个变量,分别为: 1. 一个线程变量名,被创建线程的标识 2. 线程的属性指针,缺省为NULL即可 3. 被创建线程的程序代码 4. 程序代码的参数 For example: - pthread_t thrd1? - pthread_attr_t attr? - void thread_function(void argument)? - char *some_argument? pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument); 结束线程 pthread_exit 线程结束调用实例:pthread_exit(void *retval); //retval用于存放线程结束的退出状态 线程等待 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); example1: 1 /************************************************************************* 2 > F i l e N a m e: t h r e a d_h e l l o_w o r l d.c 3 > A u t h o r: c o u l d t t(f y b y) 4 > M a i l: f u y u n b i y i@g m a i l.c o m 5 > C r e a t e d T i m e: 2013年12月14日 星期六 11时48分50秒 6 ************************************************************************/ 7 8 #i n c l u d e 9 #i n c l u d e 10 #i n c l u d e

11 12 v o i d p r i n t_m e s s a g e_f u n c t i o n (v o i d *p t r)? 13 14 i n t m a i n() 15 { 16 i n t t m p1, t m p2?

相关文档
最新文档