1、基本概念
信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。
同样在threading中,信号量有acquire和release两个函数。
- 每当线程想要读取关联了信号量的共享资源时,必须调用acquire,此操作减少信号量的内部变量,如果此变量的值非负,那么分配该资源的权限。如果是负值,那么线程被挂起,直到有其他的线程释放资源。
- 当线程不再需要该共享资源,必须通过release释放,这样,信号线的内部变量增加,在信号量等待队列中排在最前面的线程会拿到共享资源的权限。
信号量同步机制在线程操作为原子操作时,才会没有问题,但如果不是原子操作,或者两个操作有一个终止了,就会出现问题,比如:
有两个并发线程,都在等待一个信号量,假设目前信号量的内部值为1,再假设线程A将信号量的值从1减到0,此时线程A拿到资源权限,这时候如果控制器切换到了线程B,线程B将信号量的值从0减到-1,并且在这里被挂起等待,这时控制器回到线程A,信号量已经成为了负值,于是第一个线程也在等待。尽管当时的信号量是可以让线程访问资源的,但是因为非原子操作导致了所有的线程都在状态。
2、信号量的使用
使用信号量进行线程同步例子:
# coding: utf-8
import threading
import time
import random semaphore = threading.Semaphore(0) def consumer():
print("Consumer is waiting.")
semaphore.acquire()
print("Consumer notify: Consumed item number %s" %item) def producer():
global item
time.sleep(10)
item = random.randint(0, 100)
print("Producer notify: Produced item number %s" %item)
semaphore.release() if __name__ == "__main__":
for i in range(0, 5):
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
print("Program terminated")
信号量被初始化为0,semaphore = threading.Semaphore(0),目的是同步两个或多个线程。线程必须并行运行,所以需要信号量同步。
如果信号量的计数器到了0,就会阻塞acquire方法,直到得到另一个线程的通知。如果信号量的计数器大于0,就会对这个值-1然后分配资源。
3、补充
信号量的一个特殊用法是互斥量。互斥量是初始值为1的信号量,可以实现数据、资源的互斥访问。
信号量在支持多线程的编程语言中应用很广,但是他也有可能造成死锁的情况。例如,有一个线程t1,先等待信号量s1,然后等待信号量s2,而线程t2会先等待信号量s2,然后再等待信号量s1,这样就会发生死锁,导致t1等待s2,但是t2在等待s1。