信号量

什么是信号量

相信课上大家已经对信号量有了一定的了解(没听课的找网课复习一下),这里再介绍一下信号量.

信号量是一种抽象数据类型,由一个整型(sem)变量和两个原子操作组成;

P(sem)(Prolaag,荷兰语尝试减少)

  • sem减1

  • 如sem<0,进入等待,否则继续

V(sem)(Verhoog,荷兰语增加)

  • sem加1

  • 如sem<=0,唤醒一个等待进程

可以这样理解,一群人在吃苹果,服务员会提供苹果。如果某个人对一个信号量执行一个P操作,就是尝试获取一个苹果,那么如果发现苹果不够了,就趴着冬眠;而服务员对一个信号量进行一个V操作,就相当于在桌子上放一个苹果,如果发现有人在冬眠中等待苹果,就叫醒一个人。而sem的初始值,实际上可以理解成桌子上初始的苹果数量。

信号量的实现(伪代码):

class Semaphore {
	int sem;
	WaitQueue q;
}

Semaphore::P(){
  sem--;//消耗一个苹果
  if(sem < 0){//不够了
    Add this thread t to q;
    block(t)
  }
}

Semaphore::V(){
  sem++;//添加一个苹果
  if(sem <= 0){//发现有人在冬眠等苹果
    Remove a thread t from q;
    wakeup(t);
  }
}+

信号量的一些应用

临界区互斥访问

我们可以利用信号量实现一把锁(相信你们学过),用来互斥访问临界区

把信号量的初值设置为1,相当于一把锁,谁得到了就可以进入临界区,得不到的就要等待。然后那个有锁的人,用V操作释放这把锁,其他人才能得到它。

mutex = new Semaphore(1);
mutex->P();//lock(&lk),获取
Critical Section;
mutex->V();//unlock(&lk),释放

这里要注意必须成对使用P()操作和V()操作:

  • P()操作保证互斥访问临界资源

  • V()操作在使用后释放临界资源

  • PV操作不能次序错误、重复或遗漏

用信号量实现条件同步

条件同步设置一个信号量,其初值为0

condition = new Semaphore(0);

下面是A和B线程的一些执行序列:

  thread A                                 thread B

... M ...

... N ...                                ... X ...

                                         ... Y ...

我们可以看到AMN模块,BXY模块,这里为了保证B执行到X后,A才能执行N,可以使用信号量实现条件同步。其中在N执行前进行P操作,然后A进入等待,B执行完X后,使用V操作唤醒A。

  thread A                                 thread B

... M ...
condition->P();  -----------+
... N ...                   |            ... X ...
                            +--------->  condition->V();
                                         ... Y ...

Last updated