用处
信号量semaphore
是用于控制进入数量的锁。有哪些应用场景呢,比如说在读写文件的时候,一般只能只有一个线程在写,而读可以有多个线程同时进行,如果需要限制同时读文件的线程个数,这时候就可以用到信号量了(如果用互斥锁,就是限制同一时刻只能有一个线程读取文件)。又比如在做爬虫的时候,有时候爬取速度太快了,会导致被网站禁止,所以这个时候就需要控制爬虫爬取网站的频率。
实例
semaphore
内部维护了一个条件变量condition
,构造函数是:
Semaphore(value=1) # value设置是内部维护的计数器的大小,默认为1.
主要有两个方法:
每当调用acquire()时,内置计数器-1,直到为0的时候阻塞
每当调用release()时,内置计数器+1,并让某个线程的acquire()从阻塞变为不阻塞
用爬虫来举例,假如说有一个UrlProducer
线程,爬取url
,多个htmlSpider
线程,爬取url
对应的网页。如果直接开20个htmlSpider
线程,20个线程是同时执行的,现在要限制同时执行能执行三个,就可以使用信号量来控制:
import threading
import time
class htmlSpider(threading.Thread):
def __init__(self, url, sem):
super().__init__()
self.url = url
self.sem = sem
def run(self):
time.sleep(2)
print("got html text success")
self.sem.release() # 内部维护的计数器加1,并通知内部维护的conditon通知acquire
class UrlProducer(threading.Thread):
def __init__(self, sem):
super().__init__()
self.sem = sem
def run(self):
for i in range(20):
self.sem.acquire() # 内部维护的计数器减1,到0就会阻塞
html_thread = htmlSpider("http://baidu.com/{}".format(i), self.sem)
html_thread.start()
if __name__ == "__main__":
sem = threading.Semaphore(3) #设置同时最多3个
url_producer = UrlProducer(sem)
url_producer.start()
从结果可以看出,每次都几乎是三个同时的完成任务。
源码分析
-
init方法
sepaphoren init方法.png
Semaphore
类的构造函数传入接收一个参数value
,设置内部计数器的大小。调用release()
时将这个值加1,调用acquire()
时减1。 -
wait方法
wait方法.png
wait
方法并不复杂,每次调用acquire
,内部技术器就减1,当计数器为0的时候,就等待通知。 -
release方法
sepaphoren release方法.png
在看release
方法的时候,突然发现,条件变量的notify
方法,在没有线程等待的时候,也是可以调用的,不用抛出异常。所以这里不用判断内部计数器是不是0,而是每次都可以调用notify
。总结
- 信号量
semaphore
可以控制同时运行执行的线程数量。 - 信号量
semaphore
内部维护了一个条件变量和一个计数器。
参考
- 信号量
网友评论