使用场合

信号量是一种非常灵活的同步方式,可以运用在多种场合中。形成锁,同步,资源计数等关系,也能方便的用于线程与线程,中断与线程的同步中。

线程同步

线程同步是信号量最简单的一类应用。例如,两个线程用来进行任务间的执行控制转移,信号量的值初始化成具备0个信号量资源实例,而等待线程先直接在这个信号量上进行等待。

当信号线程完成它处理的工作时,释放这个信号量,以把等待在这个信号量上的线程唤醒,让它执行下一部分工作。这类场合也可以看成把信号量用于工作完成标志:信号线程完成它自己的工作,然后通知等待线程继续下一部分工作。

锁,单一的锁常应用于多个线程间对同一临界区的访问。信号量在作为锁来使用时,通常应将信号量资源实例初始化成1,代表系统默认有一个资源可用。当线程需要访问临界资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问临界区的线程将被挂起在该信号量上,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。

因为信号量的值始终在1和0之间变动,所以这类锁也叫做二值信号量,如图 所示:

锁

中断与线程的同步

信号量也能够方便的应用于中断与线程间的同步,例如一个中断触发,中断服务例程需要通知线程进行相应的数据处理。这个时候可以设置信号量的初始值是0,线程在试图持有这个信号量时,由于信号量的初始值是0,线程直接在这个信号量上挂起直到信号量被释放。当中断触发时,先进行与硬件相关的动作,例如从硬件的I/O口中读取相应的数据,并确认中断以清除中断源,而后释放一个信号量来唤醒相应的线程以做后续的数据处理。例如finsh shell线程的处理方式,如图 finsh shell的中断、线程间同步 所示:

finsh shell的中断、线程间同步

semaphore先初始为0,而后shell线程试图取得信号量,因为信号量值是0,所以它会被挂起。当console设备有数据输入时,将产生中断而进入中断服务例程。在中断服务例程中,它会读取console设备的数据,并把读得的数据放入uart buffer中进行缓冲,而后释放信号量,释放信号量的操作将唤醒shell线程。在中断服务例程运行完毕后,如果系统中没有比shell线程优先级更高的就绪线程存在时,shell线程将持有信号量并运行,从uart buffer缓冲区中获取输入的数据。

  • 警告:中断与线程间的互斥不能采用信号量(锁)的方式,而应采用中断锁。

    资源计数

资源计数适合于线程间速度不匹配的场合,这个时候信号量可以做为前一线程工作完成的计数,而当调度到后一线程时,它可以以一种连续的方式一次处理数个事件。例如,生产者与消费者问题中,生产者可以对信号进行多次释放,而后消费者被调度到时能够一次处理多个资源。

  • 注:一般资源计数类型多是混合方式的线程间同步,因为对于单个的资源处理依然存在线程的多重访问,这就需要对一个单独的资源进行访问、处理,并进行锁方式的互斥操作。