精华内容
下载资源
问答
  • wait如何实现进程同步
    千次阅读
    2019-10-02 15:23:31

     

    进程同步是指对多个相关进程在执行次序上进行协调,以使并发执行的主进程之间有

    效地共享资源和相互合作,从而使程序的执行具有可在现行。

    首先,程序在调用fork()机那里了一个子进程后,马上调用wait(),使父进程在子进程调

    用之前一直处于睡眠状态,这样就使子进程先运行,子进程运行exec()装入命令后,然后调用wait(0),使子进程和父进程并发执行,实现进程同步。

    转载于:https://www.cnblogs.com/kazama/p/10734962.html

    更多相关内容
  • 特殊进程 Linux系统中有两种特殊进程:僵尸进程、孤儿进程; 僵尸进程:当程序调用exit()函数后,该进程并不是马上就消失, 而是留下一个称为僵尸进程的数据结构。僵尸进程是一种特殊的 进程,他几乎放弃进程退出前...

    特殊进程
    Linux系统中有两种特殊进程:僵尸进程、孤儿进程;

    僵尸进程:当程序调用exit()函数后,该进程并不是马上就消失,
    而是留下一个称为僵尸进程的数据结构。僵尸进程是一种特殊的
    进程,他几乎放弃进程退出前占用的所有内存,既没有可执行代
    码也不能被调度,只在进程列表中留下一个位置,记载进程退出
    状态等信息供父进程收集。若父进程中没有回收子进程的代码,
    子进程会一直处于僵尸状态。子进程退出后能释放用户区,但
    不能释放内核区的一些数据。
    
    孤儿进程:父进程死了,子进程还或者,子进程就变成了孤儿进程。
    此时,在较低的一些Linux版本,子进程会被init进程领养,但较高
    的一些版本被一个特殊的进程领养;
    

    特殊进程的危害

    特殊进程不能再次运行。却会占据一定的内存空间,当系统中的僵尸进程数量过多时,不仅会占据系统内存,还会占用进程id;因此使用wait()、waitpid()可以避免僵尸进程产生。

    如果僵尸进程已经产生,通常解决僵尸进程的方法就是终止其父进程。这样僵尸进程就会作为孤儿进程被init进程(现在的版本可能不是init进程领养,总之会有一个特殊的系统进程领养),这个特殊的进程会不断调用wait()函数来获取子进程的状态,收集退出的子进程的状态,并清理它占用的空间。最后,孤儿进程永远不可能称为僵尸进程;

    wait()函数

    pid_t wait(int * status);
    

    返回值:调用成功,返回捕捉到的僵尸态子进程的id;调用失败,返回-1,errno被设置为ECHILD;

    参数status:status是一个传出参数,用于获取子进程的退出状态,如果不关心子进程的退出状态可以传入NULL;

    调用wait()函数的进程会被挂起,进入阻塞状态。直到子进程变成僵尸进程,wait()函数捕捉到子进程的退出状态才会转变到运行状态,回收子进程的资源并返回;若没有变为僵尸态的子进程,wait()函数会让进程一直阻塞。当前进程如果有多个子进程,只要捕捉到一个变为僵尸态的子进程信息,wait()函数就会返回子进程id恢复执行状态;

    子进程的退出状态被存放在exit()函数的status参数的低八位中。使用常规方法读取比较麻烦,所以Linux定义了两个宏,用于获取进程退出状态。
    WIFEXITED(status);用于判断子进程是否正常退出,如果是,返回非零值,否则返回零;
    WEXITSTATUS(status);WIFEXITED()函数通常与WEXITSTATUS()函数搭配使用;若WIFEXITED()返回非0(即正常退出),则可以使用WEXITSTATUS()提取子进程的返回值;
    WIFSIGNALED(status) 如果子进程因为一个未捕获的信号而终止,它就返回真;否则返回假。
    WTERMSIG(status) 如果WIFSIGNALED(status)为真,则可以用该宏获得导致子进程终止的信号代码。
    WIFSTOPPED(status) 如果当前子进程被暂停了,则返回真;否则返回假。
    WSTOPSIG(status) 如果WIFSTOPPED(status)为真,则可以使用该宏获得导致子进程暂停的信号代码。

    waitpid()函数

    pid_t wait(pid_t pid,int * status,int options);
    

    pid>0时,只等待pid与该参数相同的子进程,如果该子进程一直没有退出,那么父进程会一直阻塞;
    pid=0时,会等待同一个进程组的子进程,若子进程加入了其他进程组,waitpid()不再关心它的状态;
    pid=-1时,waitpid()与wait()函数相同,将阻塞等待并回收一个子进程;
    pid<-1时,会等待指定进程组的任何子进程,进程组的id等于pid的绝对值;

    int options

    参数options提供了一些另外的选项来控制waitpid()函数的行为。如果不想使用这些选项,则可以把这个参数设为0。
    主要使用的有以下两个选项:

    WNOHANG 如果pid指定的子进程没有结束,则waitpid()函数立即返回0,而不是阻塞在这个函数上等待;如果结束了,则返回该子进程的进程号;

    WUNTRACED 如果子进程进入暂停状态,则马上返回。
    这些参数可以用“|”运算符连接起来使用。

    如果waitpid()函数执行成功,则返回子进程的进程号;如果有错误发生,则返回-1,并且将失败的原因存放在errno变量中。

    失败的原因主要有:没有子进程(errno设置为ECHILD),调用被某个信号中断(errno设置为EINTR)或选项参数无效(errno设置为EINVAL)
    如果像这样调用waitpid函数:waitpid(-1, status, 0),这此时waitpid()函数就完全退化成了wait()函数。

    展开全文
  • 进程同步讲起,阐述了同步的意义,进而详细介绍了信号量如何使用从而保证进程同步


    进程同步

    进程同时处理同一串数据, 会造成不确定性,比如有多个进程同时对一个文件进行读写,那么读文件的进程无法确定自己读到的数据是否是它本来想要的数据,还是被修改的数据,除此以外,当先读后写时,由于缓冲区没有写入数据,读进程无数据可读,就会因此被阻塞(使用管道通信)。

    这种两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精准时序,称为数据竞争,而这种多个程序可以并发执行,但是由于系统资源有限,程序的执行不是一贯到底的,以不可预知的速度向前推进,这又被称为异步性

    这种受访问顺序影响的数据是没有意义的(程序的运行不能有二义性),所以为了能够使得进程有能够有一定的顺序来访问数据,从而引入了同步的概念。

    所谓进程同步就是指协调这些完成某个共同任务的并发线程,在某些位置上指定线程的先后执行次序、传递信号或消息

    本文将主要讲解如何使用信号量实现进程同步。

    信号量基本概念

    信号量相当于一个信号灯,在程序实现中往往是一个非负整数。在实际生活中,如火车进站前会看到的信号灯,若灯亮说明火车可以进站,否则不能进站,这里的信号灯就可以看作是信号量,火车看作是进程,能否进站即能否访问资源。

    在进程进入一个关键代码段之前,进程必须获取一个信号量;一旦该关键代码段执行完毕了,那么该线程必须释放信号量。其它想进入该关键代码段的进程必须等待直到第一个进程释放信号量。

    信号量

    • 作用:控制多进程共享资源的访问(资源有限并且不共享)
    • 本质:任一时刻只能有一个进程访问临界区(代码),数据更新的代码。

    PV操作

    PV操作即是针对信号量进行的相应操作,PV操作由P操作原语和V操作原语组成(原语是不可中断的过程)。

    当进程执行P操作,若信号量大于零(有共享资源),则信号量减一,进程继续执行;若信号量为零,则进程等待。
    在这里插入图片描述
    当进程执行V操作,若信号量大于零(有共享资源),则信号量加一;若信号量为零,则唤醒等待进程。如下图所示:

    在这里插入图片描述

    关于信号量的函数

    使用信号量的相关函数时需要添加头文件#include <semapore.h>,链接库为pthread

    接下来我们具体学习关于信号量的相关函数,根据信号量是否命名分为命名信号量(基于文件实现)和匿名信号量(基于内存)。

    命名信号量相关函数

    操作函数定义
    创建sem_t *sem_open(const char *name, int oflag, mode_t mode,unsigned int value)
    删除int sem_unlink(const char *name)
    打开sem_t *sem_open(const char *name, int oflag)
    关闭int sem_close(sem_t *sem)
    挂出int sem_post(sem_t *sem)
    等待int sem_wait(sem_t *sem)
    尝试等待int sem_trywait(sem_t *sem)
    获取信号量的值int sem_getvalue(sem_t *sem, int *sval)

    匿名信号量相关函数

    操作函数
    初始化int sem_init (sem_t *sem , int pshared, unsigned int value)
    销毁int sem_destroy(sem_t *sem)
    挂出int sem_post(sem_t *sem)
    等待int sem_wait(sem_t *sem)
    尝试等待int sem_trywait(sem_t *sem)
    获取信号量的值int sem_getvalue(sem_t *sem, int *sval)

    信号量的使用

    命名信号量

    我们首先创建两个进程,具体代码如下:

    #include <iostream>
    #include <unistd.h>
    
    using namespace std;
    
    int main(){
        fork();
        for(int i=0; i<5; i++){
            cout << getpid() << ":before"  << endl;
            sleep(1); // 模拟耗时操作
            cout << getpid() << ":before"  << endl;  
        }
    }
    

    执行结果如下:
    在这里插入图片描述
    显然pid为6099和6100的两个进程的执行顺序显然是不受控制的,接下来我们借用信号量来限制两个进程的执行顺序,具体代码如下(代码备注中有关于函数的细节使用):

    #include <iostream>
    #include <unistd.h>
    #include <semaphore.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    
    using namespace std;
    int main(){
        sem_t* p = sem_open("/test",O_CREAT|O_RDWR,0666,1);
        // 信号量命名必须是以/开头,其并不是根目录的意思,该文件存于/dev/shm文件下
        // 标志位O_CREAT|O_RDWR有两个作用:该信号量不存在就创建,存在就打开
        // 0666为该信号量文件的权限
        // 1 为信号量个数(也可以设置为其他正整数)
        fork();
        for(int i=0; i<5; ++i){
            usleep(100);
            sem_wait(p);  // P操作,出现阻塞,初始值减一
            // 临界区开始
            cout << getpid() << "before" << endl;
            sleep(1); //模拟耗时操作
            cout << getpid() << "after" << endl;
            // 临界区结束
            sem_post(p); // 信号量初始值+1,唤醒阻塞进程,阻塞进程唤醒不定
        }
        sem_close(p);
        p = NULL;
    }
    

    执行结果如下:
    在这里插入图片描述
    显然此时一个进程进入临界区执行操作离开后另一个进程才会开始执行,由此两个进程开始有序执行,实现了同步操作。

    需要注意的是,在代码备注中我们也提到了,创建的文件将会默认在/dev/shm下创建,且某些系统默认将会在该文件名前增加sem.前缀,我们可以使用ls查看,如下图:

    在这里插入图片描述

    匿名信号量

    以上我们使用了一个命名的信号量来实现了进程同步,除此以外,我们也可以使用匿名的信号量来实现同步。

    我们知道fork所生成的父子进程的内存资源是共享的,故而我们可以借助这个特点,来创建一个父子进程都可以访问到的内存来初始化信号量,这里我们就需要借助匿名的共享内存来实现这个操作。(如果需要了解共享内存相关知识,可以查看:【进程间通信2】使用共享内存实现进程间的通信(附C++实现代码)

    故而,匿名信号量是基于共享内存来实现的,它不涉及需要指定某个特定文件,故而创建这种匿名信号量不会生成/dev/shm下的文件。

    我们给出具体代码如下:

    #include <iostream>
    #include <unistd.h>
    #include <semaphore.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    
    using namespace std;
    
    int main(){
        sem_t* p = (sem_t*)mmap(NULL,sizeof(sem_t),PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANON,-1,0);  // 申请动态内存
        sem_init(p,1,1);  
        // 创建匿名信号量,必须要对信号量进行初始化
        // 第一个1指的是进程间的信号量(0的话表示是线程)
        // 第二个1指的是信号量个数
        fork();
            for(int i=0; i<5; ++i){
            usleep(100);
            sem_wait(p);  // 出现阻塞,初始值减一
            cout << getpid() << "before" << endl;
            sleep(1); //模拟耗时操作
            cout << getpid() << "after" << endl;
            sem_post(p); // 信号量初始值+1,唤醒阻塞进程,阻塞进程唤醒不定
        }
        sem_close(p);
        sem_destroy(p);  // 销毁信号量
        munmap(p,sizeof(sem_t)); // 释放申请的内存
        p = NULL;
    }
    

    执行结果如下:

    在这里插入图片描述

    多值信号量

    在实际生活中的资源往往不只一个,在这部分我们针对停车场这个应用场景,在停车场没有车位的情况下,车无法进入停车场停车,如有车位则正常停车。具体代码如下:

    park.cpp:用于创建多值信号量。模拟创建停车场的过程,信号量个数就是我们的停车场车位个数。

    注意这里只需要创建,所以sem_open函数中的标志位为O_CREAT

    #include <iostream>
    #include <semaphore.h>
    #include <fcntl.h>
    
    using namespace std;
    
    // a.out name num 
    // name:信号量名
    // num:信号量个数
    int main(int argc, char* argv[]){
        if(3!=argc){
            printf("Usage:%s name num\n",argv[0]);
            return 1;
        }
        sem_open(argv[1], O_CREAT, 0666, atoi(argv[2]));
    }
    

    需注意该文件运行的命令格式为可执行文件名 创建的信号量文件名 该文件的信号量个数,我在我的命令行的创建命令为./a.out /park 5

    car.cpp:模拟汽车出入停车场的过程,进入一辆车之后车位减少一个

    // a.out name num 
    // name:信号量名称
    int main(int argc, char* argv[]){
        if(2!=argc){
            printf("Usage:%s name",argv[0]);
            return 1;
        }
        sem_t* p = sem_open(argv[1],O_RDWR);
        sem_wait(p); // 进入停车场,占用一个车位
        cout << getpid() << ":enter" << endl;
        sleep(3);  // 模拟停车时间
        sem_post(p);  // 出停车场,得到一个空车位
        cout << getpid() << ":exit" << endl;
        sem_close(p);
    }
    

    然后我们就可以运行代码如下:

    在这里插入图片描述

    展开全文
  • Hello,你好呀,我是灰小猿,一个超会写bug的程序猿! 今天是10.24程序员节????????????! 我要把你的名字写进代码里?...那么身为一名浪漫的程序员,应该如何深入的实现进程同步嘞?今天就来和小

    Hello,你好呀,我是灰小猿,一个超会写bug的程序猿!

    今天是10.24程序员节🙊🙊🙊!

    我要把你的名字写进代码里🖥,因为我是浪漫的程序员🙊!

    我要把你种进我的头发里💻,因为我是浪漫的程序员🙊!

    我要把你编程键盘上的CV⌨️,因为我是浪漫的程序员🙊!

    我要把你new在对象里🖱,因为我是浪漫的程序员🙊!

    我还要我们的进程同步💾,因为我是浪漫的程序员🙊!

    那么身为一名浪漫的程序员,应该如何深入的实现进程同步嘞?今天就来和小猿一探究竟吧😇文中1024程序员表情包大赏

    1 问题描述

    1.1 为什么要有进程同步

    在我们使用的操作系统中为什么要有进程同步机制?我们的计算机系统刚开始是单道批处理系统,意思就是同一时间段内只能运行一个程序,这个程序运行完,才能运行另一个程序,这样就会导致运行效率太低,系统中的资源得不到充分的利用。这也是传统操作系统在进行业务处理的时候效率低下的主要原因,那么对于这种情况应该如何解决呢?这也是现在多道批处理系统出现的原因。

    多道程序并发执行,这样大大提高了系统资源的利用率。但是这种系统就会产生一些问题,比如有的资源,比如显示器,cpu,同一时间肯定只能一个程序使用,多个程序肯定不能同时使用显示器,这就是互斥关系,另外,有的两个进程间存在这样的制约关系:A程序的输出是B程序的输入,这是同步关系,为了解决这些问题,我们引入了进程同步机制。通过实现进程同步机制,我们可以实现多个进程对于资源的同步访问,提高资源的使用效率。

    1.2 进程同步的方法

    对于实现进程同步,我们常用的方法就是实现信号量机制。常用的信号量机制有三种,分别是整型信号量,结构体型信号量和AND型信号量。那么这三种信号量机制具体指什么呢?接下来我对这三种信号量机制逐一进行介绍。

    1.2.1 整型信号量

    整型信号量是指用一个整数S来进行管理,这个整数S代表资源的数目,我们往往对资源的操作方式有两种,一种是使用,一种是释放。他们分别对应两个函数:wait和signal

    wait函数的作用是首先会对资源S进行判断,看资源S可不可以用,如果不可以用,也就是S<=0,那么该进程会一直等下去,直到S成为一个正数,才会执行wait方法。然后S自减一次;而signal的意思则是使用完资源后释放资源,S自增一次。

    Wait函数和signal函数的具体实现如下:

    wait(S){
        while(S<=0);
        S--;
    }
    
    signal(S){
        S++;
    }

    整型信号量的缺点:

    从上面的程序中,我们其实可以发现一个问题,就是当信号量S是小于0的时候,在wait函数中,程序会一直的死循环下去。这样进程就会处于一个“忙等”的状态。这也是使用整型信号量机制的不足之处,那么如果要解决“忙等”,就要让程序进行“让权等待”。也就是在进行无法使用资源的时候,释放处理机,避免长时间占用。

    1.2.2 结构体型信号量

    对于上面出现的问题,我们应该让哪个进程访问临界资源呢?这个时候结构体信号量就出现了,我们可以使用一个链表来解决这个问题。使用一个结构体来描述资源,这个结构体长这样:

    typedef struct{
        int value;
        struct process_control_block *list;
    }semaphore;

    在这个结构体里边有两个变量,一个是value,用来记录资源的个数,第二个是指针,指向下一个要使用临界资源的进程。

    通过使用结构体型信号量,wait和signal函数就进行了一些改进,如下这样:

    wait(semaphore *S){
        S->value--;
        if(S->value<0)  block(S->list);
    }
    
    signal(semaphore *S){
        s->value++;
        if(S->value<=0)
          wakeup(S->list);
    }

    在程序中,S->value的初值表示系统中某类资源的数目,可称为资源信号量。

    注:

    S>0时:表示可供并发进程使用的资源数。

    S<=0时:|S|表示因缺少该资源而自我阻塞的进程数。

    在改进之后的wait函数中,首先会向操作系统申请资源,并且让资源数减一。如果资源分配完毕,那么就调用block原语将进程进行自我阻塞。

    而在signal函数中,首先会释放资源,让资源数减一,之后进行判断,如果该信号量链表中仍有等待的进程被阻塞,那么就调用wakeup原语将其唤醒。

    1.2.3 AND型信号量

    AND型信号量是将进程在整个运行过程中的所有资源,一次性全部分配给进程,进程使用完后再一次性释放,只要一个进程尚未分配成功,其他所有为之分配的资源也不分配给它。也就说要么把它所请求的资源全部分配给进程,要么一个也不分配,这样的好处是可以很好的避免死锁现象。

    使用该信号量机制时,wait和signal函数的实现如下:

    wait(S1,S2,...,Sn)
    {
        while(true)
        {
            if(Si>=1&&...&&Sn>=1)
            {
                for(i=1;i<n;i++)
                Si--;
                break;
            }
            else
            {
                place the process in the waiting queue associated with.....
                //如果没有申请到资源的话就挂到相应的阻塞队列里去
            }
        }
    }
    
    Ssignal(S1,S2,...Sn)
    {
        while(true)
        {
            for(i=1;i<=n;i++)
            {
                Si++;
                remove all the process waiting in the queue associated with Si into the ready queue;
                //去后备队列里唤醒或移除因Si资源阻塞的进程
            }
        }
    }

    以上就是对三种信号量机制的作用和实现原理的说明。

    1.3 使用信号量机制解决问题

    使用信号量机制所要解决的问题还是进程的互斥和同步问题。那么这两种问题又是如何实现的呢?

    1.3.1 实现互斥关系

    假设两个进程PA,PB具有互斥关系,也就是他们要使用通同一个临界资源,对于这种情况应该怎么做呢?我们可以设置一个mutex信号量,初值设为1,这样在最开始的时候,两个进程都能使用该资源,在进程PA使用资源的时候,首先会调用wait函数让资源数减一,wait函数完成之后会让信号量mutex-1,这样mutex的值此时为0,另一个进程PB就不能使用该资源了,在进程PA使用完资源以后会调用signal函数释放资源,让信号量mutex+1,mutex此时会重新变成1,另一个进程PB就可以使用该临界资源了。

    semaphore mutex=1;
    PA(){
        while(1){
            wait(mutex);
            临界区
            signal(mutex);
            剩余区
        }
    }
    
    PB(){
        while(1){
            wait(mutex);
            临界区
            signal(mutex);
            剩余区
        }
    }

    1.3.2 实现前驱关系

    假设P1和P2有前驱关系,P1执行完,P2才能执行,那么这种应该怎么实现呢?这时可以设置一个公共的信号量S,初值设为0。

    进程P1中:S1;signal(S);

    进程P2中:wait(S);S2;

    上面语句的意思是,先执行P1的语句,然后释放S,也就是S++,这样当P2执行完wait函数之后才可以执行,否则,不执行signal函数的话,S就为0,P2也无法执行,这样就实现了P1和P2的前驱关系。

    接下来,我们将通过实验的方式来对上面描述的实现进程同步和互斥的问题进行实验。验证这种方式的实际使用性。

    2 程序设计

    2.1 需求分析

    生活中我们经常遇到的生产者/消费者问题其实就是一个典型的进程同步问题,其中的生产者产生资源,消费者消耗资源。比如典型的买包子问题,我们可以通过它来模拟进程的同步。消费者与生产者进程之间的执行都依赖于另一个进程的消息,想要表现同步机制,这需要使用Java中的wait() / notify()方法实现同步机制。由于包子余量(资源数量)需要所有进程共享,因此任意时刻只能有一个进程访问缓冲器,这需要使用Java中的synchronized同步代码块实现,synchronized关键字的作用就是控制多个线程访问资源同步性的问题,它的三种使用方式是:修饰实例方法、修饰静态方法、修饰代码块。

    如果方法或代码块用 synchronized 进行声明,那么对象的锁将保护整个方法或代码块,要调用这个方法或者执行这个代码块,必须获得这个对象的锁。而且,任何时候都只能有一个线程对象执行被保护的代码。

    2.2 算法设计思路

    我们以买包子问题为例,现设计2个厨师(生产者),2个顾客(消费者),包子铺包子存储上限为3(缓冲器大小)。包子初始值为0,此时所有买家进程会进入等待状态,所有的厨师进程会在包子余量不超过缓冲器大小前不停做包子,并唤醒买家进程已经有包子可吃了,直至缓冲器满了进入等待状态,而买家进程每吃掉一个包子后都会唤醒厨师进程可以继续做包子了。同时由于包子余量需要所有进程共享,保证任意时刻只能有一个进程访问缓冲器,因此所有进程方法都需要用synchronized声明。

    3 源代码清单

    3.1 缓冲器公共类

    package com.common;
    
    /**
     * 定义缓冲器余量,生产者和消费者执行的方法
     */
    public class BaoZi {
        Integer count = 0;      //记录包子总数
    
        /**
         * 生产包子(产生资源)
         *
         * @param name 厨师名
         */
        public synchronized void makeBaoZi(String name) {
    //        判断包子数是否大于3达到上限
            while (count >= 3) {
                System.out.println(name + ":包子已经达到上限了!");
                try {
                    wait();     //包子容量达到3,超出上限,生产者进入等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count++;    //如果没有达到3就做一个包子
            System.out.println(name + ":做了一个包子,还剩【" + count + "】个!");
            notifyAll();    //有包子资源了,唤醒消费者购买包子
        }
    
        /**
         * 购买包子(使用资源)
         *
         * @param name
         */
        public synchronized void buyBaoZi(String name) {
    //        判断包子资源数是否等于0,
            while (count == 0) {
                System.out.println(name + ":没有包子资源了!");
                try {
                    wait();     //没有包子资源,消费者进程进入等待状态
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count--;    //包子资源数量减一
            System.out.println(name + ":购买了一个包子,包子资源剩余【" + count + "】个!");
            notifyAll();    //包子数未达到上限,让生产者厨师做包子,(唤醒所有等待该资源的进程),
        }
    }
    

    3.2 生产者进程类

    package com.producer;
    
    import com.common.BaoZi;
    
    /**
     * 生产者进程
     */
    public class BaoZiPu extends Thread {
        private String name;    //厨师名字
        private BaoZi baoZi;    //缓冲区包子资源
    
        public BaoZiPu() {
        }
    
        public BaoZiPu(BaoZi baoZi, String name) {
            this.baoZi = baoZi;
            this.name = name;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(2000);     //休眠两秒钟
                    baoZi.makeBaoZi(name);  //生产一个包子资源
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    

    3.3 消费者进程类

    package com.consumer;
    
    import com.common.BaoZi;
    
    /**
     * 消费者进程,消耗包子资源
     */
    public class Customer extends Thread {
    
        private String name;
        private BaoZi baoZi;
    
        public Customer() {
        }
    
        public Customer(BaoZi baoZi, String name) {
            this.baoZi = baoZi;
            this.name = name;
        }
    
        @Override
        public void run() {
            while (true){
                try {
                    Thread.sleep(2000); //休眠两秒钟
                    baoZi.buyBaoZi(name);   //消耗一个包子资源
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    

    3.4 测试类

    import com.common.BaoZi;
    import com.consumer.Customer;
    import com.producer.BaoZiPu;
    
    public class TestMain {
    
        public static void main(String[] args) {
            System.out.println("==========进程同步测试==========");
            BaoZi baoZi = new BaoZi();
            Thread rouBaoZi = new Thread(new BaoZiPu(baoZi, "肉包子厨师"));
            Thread suBaoZi = new Thread(new BaoZiPu(baoZi, "素包子厨师"));
            Thread customerZS = new Thread(new Customer(baoZi, "顾客张三"));
            Thread customerLS = new Thread(new Customer(baoZi, "顾客李四"));
            customerZS.start();
            rouBaoZi.start();
            suBaoZi.start();
            customerLS.start();
    
        }
    }
    

    4 运行结果测试与分析

    4.1 测试运行结果 

    4.2 实验结果分析

    根据实验测试结果我们可以做出这样的分析,在我们创建好四个进程时,让四个进程共享一个缓存区资源(包子),之后让顾客张三先去购买包子,结果包子此时是0个,那么该进程进入等待状态,之后素包子厨师和肉包子厨师访问资源开始创建包子,在创建好一个包子资源之后,此时顾客李四也开始去访问包子资源。所有就购买了一个包子。包子数剩余0个,这个时候肉包子厨师的包子做好了,所以现在会有一个包子资源,但是会被等待队列中的张三买走。此时包子资源的数量是没有达到上限的,那么包子厨师就会不断的创建包子资源,同时顾客也会不断的去买包子,直到包子数量为0时,顾客再去购买就会提醒没有包子了。

    5 结论

    设计这个实验的目的就是为了验证和测试操作系统下进程同步的问题,通过实验学习和代码实践,让我对进程间同步和互斥机制有了更加深刻的认识和理解。在这里我通过包子铺卖包子和买家买包子的案例模拟生产者/消费者问题实现进程间的同步和互斥。

    对于生产者和消费者对缓冲区的访问,都是有两个限定条件的。首先对于包子厨师能不能生产包子放到缓冲器中,需要两个条件是:第一,缓冲器需要空闲,即包子余量有没有达到上限;第二,获取当前资源对象的锁,判断有没有其他生产者或消费者在缓冲器中。对于顾客购买包子访问缓冲器也需要两个条件:第一是缓存器中存在资源,也就是有包子,第二是判断有没有其他生产者或消费者在缓冲器中,这都是需要synchronized关键字同步代码块来实现的。

    浪漫的程序员朋友。你学会了吗?评论区留言,说出你认为最浪漫的程序员情话!

    觉得不错,记得🤞🏻一键三连🤞🏻哟!

    我是灰小猿,我们下期见!💘💘💘

    展开全文
  • 进程互斥是进程同步控制中的一种特例。 1.1临界资源和临界区 临界资源:某段时间内只允许一个进程使用的资源。 临界区:使用临街资源的那一部分程序。 1.2遵循准则 为了提供对互斥的支持,系统必须满足以下条件: ...
  • 实验一 进程创建 实验目的和要求 11.了解进程的概念及意义 2.了解子进程和父进程 3.掌握创建进程的方法 实验内容 ... 在linux中fork函数时非常重要的函数它从已存在进程中创建一个新进程进程为子进程而原进程为父进程
  • Linux进程同步wait

    2021-02-06 19:23:54
    实现进程同步。 当子进程执行时, wait()可以暂停父进程的执行,使其等待。一旦子进程执行完,等待的父进程就会重新执行。如果有多个子进程在执行,那么父进程中的 wait()在第一个子进程结束时返回,恢复父进程...
  • 进程同步的两种实现

    千次阅读 2019-04-02 17:38:57
    在前文《Linux – 进程控制》博客中,我曾提到过父子进程之间的同步有两种方法:分别是基于管道和信号实现。...基于管道实现进程同步 #include <unistd.h> #include <stdio.h> #incl...
  • 详解进程同步

    千次阅读 2021-07-30 07:59:26
    进程和线程都是CPU的调度单元,所以进程同步和线程同步是一样的概念,文中不具体细分进程和线程 一、进程同步 在详解操作系统进程中,介绍过了并发进程,并发进程之间分为独立关系和交互关系,独立关系的进程分别在...
  • 操作系统--进程同步

    千次阅读 多人点赞 2022-04-14 16:00:49
    进程同步进程同步概念进程互斥的软件实现方法单标志法双标志先检查双标志后检查Peterson 算法进程互斥的硬件实现方法中断屏蔽方法TestAndSet指令Swap指令信号量机制 进程同步概念 在多道程序环境下,进程是并发执行...
  • 经典的进程同步问题详解

    千次阅读 2022-02-10 22:27:07
    2.5 经典的进程同步问题(重点!!!) 一、生产者-消费者问题 二、哲学家进餐问题 三、读者-写者问题
  • PV操作实现进程同步问题

    千次阅读 2020-06-26 17:02:26
    1.设玩具车间生产小组在一个工作台边工作,工作台上有N个位置(N≥3)用于存放车架或车轮,且每个位置只能放一件车架或...试用信号量与P、V操作实现三个工人的合作,要求解中不含死锁。 先定义信号量:为防止死锁的发生,
  • 1965年,荷兰学者Dijkstra提出了一种卓有成效的实现进程互斥、同步的方法——信号量机制 信号量其实就是一个变量(可以是一个整数,也可以是一个更复杂的变量),可以用信号量来表示系统中某种资源的数量。...
  • (3)使用系统调用wait()和exit(),实现父子进程同步。【实验原理/实验基础知识】一、同步在多道系统中,一个进程相对于另一个进程的运行速度是不确定的,但相互合作的几个进程需要在某些情况下相互协调工作。同步关系...
  • 文章目录信号量机制信号量整形信号量记录型信号量信号量机制的应用信号量机制实现进程互斥信号量机制实现进程同步信号量机制实现进程的前驱关系 信号量机制 信号量 用户可以通过使用操作系统提供的一对原语来对信号...
  • 问题描述:公共汽车上,...我们可以使用两个信号量来实现司机与售票员间的同步,具体实现形式如下:司机进程:司机开车;v(s2);p(s1);汽车离站;售票员进程:售票员售票;p(s2);售票员开车门;乘客上下车;售票员关...
  • 进程同步和通信 进程同步 在OS中引入进程后,一方面使系统的吞吐量和资源的利用率得到提升,另一方面也使得系统变得复杂,如果没有合理的方式对进程进行妥善的管理,必然会引起进程对系统资源的无序竞争,使系统...
  • linux下实现进程同步

    千次阅读 2019-04-26 18:05:42
    网上的大部分教程讲的都是线程同步,却很少有关于进程同步的博客,但其实线程同步与进程同步还是有些许差别的,故写此博客加以说明. 知识点 1.linux semaphore 头文件#include <semaphore.h> 编译注意事项:...
  • 只要把临界区置于P(sem)和V(sem)之间,即可实现进程间的互斥。 当一个进程想要进入临界区时,它必须先执行P原语操作以将信号量sem减1。 在一个进程完成对临界区的操作之后,它必须执行V原语操作以释放它所占用的临界...
  • 文章目录[1] get_cond_wait.c 初始化条件变量和锁函数[2] cond_signal.c 发送信号函数[3] cond_wait.c 等待信号函数[4] Makefile[4] 运行结果 [1] get_cond_wait.c 初始化条件变量和锁函数 #include "public.h" ...
  • 利用记录型信号量机制: wait(s), signal(s)解决进程同步问题
  • 进程同步的概念 两种形式的制约关系 1.间接相互制约。资源共享——(CPU,I/O设备,打印机) 2.直接相互制约。进程合作——输入(A)与计算(B)进程(A->单缓冲->B) 临界资源 定义:进程在使用它们时,进程间采用...
  • public class S03 { private boolean isSub = true; public synchronized void sub() throws InterruptedException { ... this.wait(); } // do something isSub = !isSub; this.notify
  • 与整形量不同,信号量的初值为非负整数,仅能通过两个标准的原子操作wait(s)和signal(s)来访问,也被称为P,V操作,信号量S的值仅有这两个操作来实现。存在忙等现象 wait(s){ while(s<=0); s--; } signal(s) { s...
  • 1. 什么是进程同步 进程具有异步性的特征, 各个并发执行的进程以独立的不可预知的速度推进 进程同步机制是为了解决进程异步的问题, 也被称为直接制约关系 2. 什么是进程互斥 OS中的某些资源虽然可以提供给多个进程...
  • 经典的进程同步问题

    万次阅读 多人点赞 2018-08-04 14:23:33
    经典的进程同步问题 普通版:一类进程作为生产者,生产产品,生产的产品放入一个缓冲区,消费者从缓冲区中取出产品,需要保证生产者不可以向满的缓冲区中添加产品,消费者不可以从空的缓冲区中取出产品。同一时刻...
  • 1.什么是进程同步,为什么要引入进程同步? 进程同步:同步也叫做直接制约关系,是协调它们的工作次序而等待、传递信息所产生的制约关系,这种关系源于它们之间的相互合作。 引入原因:在多道程序环境下,进程是...
  • 用信号量机制分别实现读者优先和写者优先的读者-写者问题从而掌握互斥与同步的基本理念。 二、实验内容 在Windows 环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应...
  • 模拟windows操作系统进程同步,吃水果事件。爸爸放苹果,妈妈放桔子,女儿吃苹果,儿子吃桔子。
  • mutex2=1:实现顾客进程对沙发的互斥访问 mutex=1:实现理发师进程对客户的互斥访问 同步信号量: 理发师与顾客的同步 empty=1:是否有空闲理发椅 full=0:理发椅上是否坐有等待理发的顾客 顾客和收营员的同步 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 139,952
精华内容 55,980
热门标签
关键字:

wait如何实现进程同步