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) //将锁变量初始化为1
for(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 something
V(&w);
}
}