tornado 中的 信号量
1 自己手动 释放
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.locks import Semaphore
sem = Semaphore(2)
async def worker(worker_id):
await sem.acquire()
try:
print("Worker %d is working" % worker_id)
await use_some_resource()
finally:
print("Worker %d is done" % worker_id)
sem.release()
async def runner():
# Join all workers.
await gen.multi([worker(i) for i in range(3)])
IOLoop.current().run_sync(runner)
2 使用上下文管理 with 自动释放
async def worker(worker_id):
async with sem:
print("Worker %d is working" % worker_id)
await use_some_resource()
# Now the semaphore has been released.
print("Worker %d is done" % worker_id)
信号量 理解
上面的tornado sem = Semaphore(2) 允许激活2个这个协程
现在放入线程中来说, 线程间同步之信号量Semaphore
信号量是控制进入数量的锁。 应用场景, 比如再读写文件的时候, 一般只能有一个文件在写, 而读可以有多个线程同时进行,如果需要限制同时读取文件的个数, 这个时候可以用到信号量(如果用互斥锁, 就是限制同一时刻只能有一个线程读取文件)。又比如再做爬虫的时候, 有时候跑去速度太快, 会被网站禁止, 这个时候就要控制爬取的频率的。
semaphore 是一个内部计数器
- 每当调用acquire()时候, 内置计数器 减1
- 每当调用release()时候, 内置计数器 加1
计数器不能 小于0 当计数器为0时候, acquire() 将阻塞线程直到其他线程调用release()
如果在主机执行IO密集型任务的时候再执行这种类型的程序时,计算机就有很大可能会宕机。
这时候就可以为这段程序添加一个计数器功能,来限制一个时间点内的线程数量。
当我们在分析网络代码客户端来说, 一定要使用某种限流机制, 防止向服务器发起太多并发请求。如果服务器负载过大, 那么系统的整体性能可能会降低。在多线程中可以使用 线程池, 在异步请求中就像tornado 中使用信号量。限制协程的数量,Semaphore 对象维护着一个内部计数器,若在对象上调用 .acquire() 协程方法,计数
器则递减;若在对象上调用 .release() 协程方法,计数器则递增。计数器的初始值在实
例化 Semaphore 时设定
就像asyncio 中信号量
semaphore = asyncio.Semaphore(concur_req)
如果计数器大于零,那么调用 .acquire() 方法不会阻塞;可是,如果计数器为零,那么
.acquire() 方法会阻塞调用这个方法的协程,直到其他协程在同一个 Semaphore 对象
上调用 .release() 方法,让计数器递增
总结:
在多线程中
信号量semaphore 可以控制同时运行执行的线程数量。
信号量semaphore内部维护了一个条件变量和一个计数器。