PV 线程同步和多线程问题

来源:互联网 发布:菲诗小铺的洗发水知乎 编辑:程序博客网 时间:2024/06/05 13:28

     PV操作:

        基于信号量的线程同步问题,主要用到P和V操作

        信号量s是具有非负整数值的全局变量,它只能由两类特殊的操作来处理。这两种操作分别称为P和V;、

       P(s)

        如果当s是非0的,那么P将s-1,并且立即返回。如果开始s就是0,那么就挂起这个线程等待。

        V(s)

       将s加1,如果有线程阻塞在P操作等待s变为非0,V操作重启这些线程中的一个。

        具体看一个例子,加以理解,想象常用的线程加锁的场景,

        

sem_t mutex;               //定义一个mutex的锁变量sem_init(&mutex,0,1)       //将锁变量初始化为1for(int i=0;i<n;i++){    P(&mutex);    cnt++;    V(&mutex);}
在这里我们首先将mutex初始化为0;想象一下多个线程在同时执行,有时可能会出现竞争的情况。

考虑刚开始执行,此时第一个线程开始运行,P(&mutex)它首先将mutex-1,因为初始时mutex是1,大于0,P操作将立刻返回,执行cnt++;都知道,在执行cnt++时,其实不是原子操作,它先将cnt的值拷贝进入寄存器,再寄存器值加1,再将寄存器的值拷回内存,完成加1操作,尽管时间很短,这还是需要时间的。此时,假设加1还未完全完成,另一个线程想要执行了,它先执行P(&mutex),发现mutex本身就是0;没有办法,它只有挂起自己,等待。由此保证了cnt++操作不会被别的线程打扰,PV操作实现锁的功能。在cnt++完成之后,V(&mutex)将mutex加1,使得mutex又变回1.同时,如果还有别的线程阻塞在P操作,正如我们刚刚说自己将自己挂起的那个线程。此时V操作可以重启这些挂起的线程中的一个。

   读者_写者问题

      一组并发的线程要访问一个共享对象,例如访问一个磁盘上的数据。有的线程只读对象,而有 的线程只修改对象,修改对象的线程叫做写者,只读的线程叫做读者。写者必须拥有对对象独占的访问,而读者可以和无限其它的读者共享对象。一般来说,有无数个并发的读者和写者。
     读者问题有几个变种,分别是关于读者的优先级和写者的优先级的。第一类:读者优先,如果有写者,当前不读。但是只要有一个读者再读,不管有没有写者在等待,必须等到所有读者读完了才允许写。第二种:写者优先,要求一旦一个写者准备好可以写,它就要尽快安排写的操作。与第一类问题不同,在第一个写者到达后读者必须等待,即使写者也是在等待。

第一类读者优先实例代码,解释下思路

int readcnt;sem_t mutex,w;void read(void){    while(1){            P(&mutex);    readcnt++;    if(readcnt==1)    P(&w);    //如果之前没有读者,此时将w相当于减1,w的值为0,当有写者到来时,检测到w为0,必须挂起等待    V(&mutex);    //read something    P(&mutex);    readcnt--;    if(readcnt==0)    V(&w);  //直到已经没有读者了,才相当与释放写者的锁,使得写者才开始            V(&mutex);}}void write(void){    while(1)    {        P(&w);//write somethingV(&w);    }}





0 0