网络编程十:线程间的同步之信号量
总结:
- Lock:一个Lock对象有两个方法acquire和release来控制共享数据的读写权限。
- Event:一个进程发事件的信号,另一个进程等待事件的信号。Event对象有两个方法set和clear来管理自己内部的变量。
- Condition:此对象用来同步部分工作流程,在并行的进程中,有两个基本的方法,wait()用来等待进程,notify_all用来通知所有等待此条件的进程。
- Semaphore:用来共享资源,比如:支持固定数据的共享连接。
- RLock:递归锁对象,其用途和方法同Threading模块一样。
- Barrier:将程序分成几个阶段,适用于有些进程必须在某些特性进程之后执行,处于Barrier之后的代码不能同处于Barrier之前的代码并行。
信号量:threading.Semaphore
import threading s = threading.Semaphore(3) # 设置信号量:假设信号量为3,那么可加锁3次,3次后将阻塞 s.acquire() # 返回True s.acquire() # 返回True s.acquire() # 返回True s.acquire() # 当加锁的次数超过信号量后,将阻塞,直到有线程释放锁
在加锁的时侯,如果指定blocking参数为False,则当加锁的次数超过信号量后,不阻塞,返回False。
blocking默认为True
s = threading.Semaphore(3) # 设置信号量:假设信号量为3,那么可加锁3次,3次后将阻塞 s.acquire(False) # 返回True s.acquire(False) # 返回True s.acquire(False) # 返回True s.acquire(False) # 当加锁的次数超过信号量后,不阻塞,但返回False
锁是信号量的特例,是信号量为1的特例。
信号量,不是rlock。信号量是可以在不同线程之间同步,rlock是只能在同一线程多次加锁。
示例:实现一个并发线程情形下的简单连接池。
注意:
- 当连接池只有剩下一个连接时,多个连接请求的问题
- 多个线程同时请求连接的争夺资源时侯,需要线程间的同步
可以用锁解决以上问题,但在这种情形下,使用信号量更好。
获取连接的时侯,加锁;释放连接的时侯,释放锁。
class ConnectionPool: def __init__(self, num): self.num = num # 初始化连接池 self.cons = [self._mak_connect(x) for x in range(num)] # 信号量 self.s = threading.Semaphore(num) def _make_connect(self, name): # 创建连接 return name def get(self): self.s.acquire() return self.conns.pop() def return_resource(self, con): self.conns.insert(con) self.s.release()
测试用例:创建最大值为3的连接池,创建5个线程从连接池中争夺连接。
import time import threading import random import logging import importlib importlib.reload(logging) logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s [%(threadName)s %(message)s]") def worker(pool): logging.info("started...") name = pool.get() logging.info("got a connection {}".format(name)) time.sleep(random.randint(1, 5)) # doing something pool.return_resource(name) logging.info("return a connection {}".format(name))
pool = ConnectionPool(3) for x in range(5): threading.Thread(target=worker, args=(pool,), name="worker-{}".format(x)).start()
结果:worker-0,1,2争夺到连接,在释放连接之前,worker-3,4等待;当worker-1连接被释放后,worker3获取到连接,worker-0释放连接后,worker4获取到连接。
2018-12-11 22:28:21,188 INFO [worker-0 started...] 2018-12-11 22:28:21,237 INFO [worker-0 got a connection 2] 2018-12-11 22:28:21,234 INFO [worker-1 started...] 2018-12-11 22:28:21,271 INFO [worker-1 got a connection 1] 2018-12-11 22:28:21,305 INFO [worker-2 started...] 2018-12-11 22:28:21,331 INFO [worker-2 got a connection 0] 2018-12-11 22:28:21,339 INFO [worker-3 started...] 2018-12-11 22:28:21,394 INFO [worker-4 started...] 2018-12-11 22:28:22,272 INFO [worker-1 return a connection 1] 2018-12-11 22:28:22,272 INFO [worker-3 got a connection 1] 2018-12-11 22:28:23,256 INFO [worker-0 return a connection 2] 2018-12-11 22:28:23,256 INFO [worker-4 got a connection 2] 2018-12-11 22:28:24,334 INFO [worker-2 return a connection 0] 2018-12-11 22:28:26,278 INFO [worker-3 return a connection 1] 2018-12-11 22:28:28,286 INFO [worker-4 return a connection 2]
信号量也是对资源的保护,但信号量和锁不一样的地方在于:
锁限制只有一个线程可以访问共享资源,而信号量限制指定个线程可以访问共享资源
可见,信号量适用于像连接池一类的“池”有关的场景。
posted on 2018-11-04 18:01 myworldworld 阅读(148) 评论(0) 收藏 举报