什么是信号量
semaphore
信号量,其控制着对公共资源或者临界区的访问。信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。
semaphore是一个内置的计数器
每当调用acquire()时,内置计数器-1;如果计数器为负数,即资源正在被占用,需要挂起等待
每当调用release()时,内置计数器+1;增加到正数时,队列中的第一个等待线程就可以访问共享资源了
模拟Lock()锁机制
如果我们将计数器设置为1即,第一次线程是不需要等待信号量的释放的,参照上节代码可以进行对比:
import threading
import time
resoure = 0
count = 1000000
semaphore = threading.Semaphore(1)
def increment():
global resoure
for i in range(count):
semaphore.acquire()
resoure += 1
semaphore.release()
def decerment():
global resoure
for i in range(count):
semaphore.acquire()
resoure -= 1
semaphore.release()
increment_thread = threading.Thread(target=increment)
decerment_thread = threading.Thread(target=decerment)
increment_thread.start()
decerment_thread.start()
increment_thread.join()
decerment_thread.join()
print(resoure)
简易生产者-消费者模型
import threading
import random
import time
semaphore = threading.Semaphore(0)
# 假设生产的资源
item_number = 0
# 消费者
def consumer():
print('Consumer is waiting for Producer')
# 等待获取信号量
semaphore.acquire()
print('get the product , number is {}'.format(item_number))
# 生产者
def producer():
global item_number
# 模拟生产资源过程
time.sleep(2)
item_number = random.randint(1, 100)
time.sleep(2)
print('made the product , number is {}'.format(item_number))
# 释放信号量
semaphore.release()
if __name__ == "__main__":
for i in range(5):
# 将生产者、消费者实例化为线程
thread_consumer = threading.Thread(target=consumer)
thread_producer = threading.Thread(target=producer)
thread_consumer.start()
thread_producer.start()
thread_consumer.join()
thread_producer.join()
print('consumer-producer example end.')
运行截图如下:
我们可以看见两个线程运行时的规律,即消费者必须等待生产者生产好商品(即释放资源),消费者才能获取消费资源(即访问资源),其余时间消费者线程都处于挂起等待(等待信号量)。
利用信号量控制并发数量
利用semaphore
我们就可以设置同时访问某些共享资源的线程数量,即通过设设置信号量的值来控制线程同时访问的数量,比如我们可以控制爬虫程序访问链接的线程数量(似乎这样可以实现一定的异步),减少目标网站的压力,同时信号量也支持上下文管理器:
import threading
import random
import time
# 信号量为三即能够释放的资源为三次
semaphore = threading.Semaphore(3) # 互斥锁+队列 相当于一个容器,容器里同时最大可以存在五个钥匙,同时也只能有五个线程,
# 谁先拿到并释放后,下一个才能拿到钥匙
# 假定url序号
order = 0
def spider():
global order
with semaphore:
# 模拟采集过程
time.sleep(2)
order +=1
print('{} is crawlering on {}th url'.format(threading.currentThread().getName(), order))
time.sleep(2)
Threads = []
for i in range(10):
t = threading.Thread(target=spider)
Threads.append(t)
t.start()
for t in Threads:
t.join()
print('Spider end.')
运行截图如下: