Python中的对象池设计模式实现
我需要一个对象池,而不是自己去实现它,我想找一个现成的、经过测试的Python库。
我发现很多其他人也在寻找这个东西,但没有得到太多直接的答案,所以我把这个问题带到Stack Overflow来了。
在我的情况下,我有很多线程(使用threading
模块),它们需要偶尔调用一个远程的基于SOAP的服务器。每个线程都可以自己建立与服务器的连接,但建立一个套接字并完成认证过程是很耗费资源的(服务器会限制连接),所以我想共享一个连接池,只在需要时再创建更多的连接。
如果要池化的对象是工作子进程,我可能会选择multiprocessing.pool
,但它们不是。如果它们是工作线程,我可能会选择这个实现,但它们也不是。
如果它们是MySQL连接,我可能会选择pysqlpool,但它们也不是。同样,SQLAlchemy Pool也不适用。
如果只有一个线程,使用可变数量的连接/对象,我会考虑这个实现,但我需要它是线程安全的。
我知道我可以很快再实现一次这个功能,但考虑到很多人都在寻找它,我觉得在Stack Overflow上有一个权威的答案会很好。
4 个回答
对于一些简单的使用场景,这里有一个基于列表的对象池模式的示例实现:
来源:
https://sourcemaking.com/design_patterns/object_pool
https://sourcemaking.com/design_patterns/object_pool/python/1
"""
Offer a significant performance boost; it is most effective in
situations where the cost of initializing a class instance is high, the
rate of instantiation of a class is high, and the number of
instantiations in use at any one time is low.
"""
class ReusablePool:
"""
Manage Reusable objects for use by Client objects.
"""
def __init__(self, size):
self._reusables = [Reusable() for _ in range(size)]
def acquire(self):
return self._reusables.pop()
def release(self, reusable):
self._reusables.append(reusable)
class Reusable:
"""
Collaborate with other objects for a limited amount of time, then
they are no longer needed for that collaboration.
"""
pass
def main():
reusable_pool = ReusablePool(10)
reusable = reusable_pool.acquire()
reusable_pool.release(reusable)
if __name__ == "__main__":
main()
我遇到过类似的问题,得说Queue.Queue这个东西还挺不错的,不过还有个小缺失。下面这个类可以帮助确保取出的对象能被放回池子里。里面有个例子。
我提供了两种使用这个类的方法,一种是用关键字,另一种是用带有析构函数的对象。推荐使用关键字的方法,但如果你因为某些原因(比如需要从多个队列中获取多个对象)不想用这个方法,至少你还有其他选择。如果你选择用带析构函数的方法,记得要注意一些常规的警告,因为析构函数可能不会被调用。
希望这能帮助到和我一样有相同问题的人。
class qObj():
_q = None
o = None
def __init__(self, dQ, autoGet = False):
self._q = dQ
if autoGet == True:
self.o = self._q.get()
def __enter__(self):
if self.o == None:
self.o = self._q.get()
return self.o
else:
return self.o
def __exit__(self, type, value, traceback):
if self.o != None:
self._q.put(self.o)
self.o = None
def __del__(self):
if self.o != None:
self._q.put(self.o)
self.o = None
if __name__ == "__main__":
import Queue
def testObj(Q):
someObj = qObj(Q, True)
print 'Inside func: {0}'.format(someObj.o)
aQ = Queue.Queue()
aQ.put("yam")
with qObj(aQ) as obj:
print "Inside with: {0}".format(obj)
print 'Outside with: {0}'.format(aQ.get())
aQ.put("sam")
testObj(aQ)
print 'Outside func: {0}'.format(aQ.get())
'''
Expected Output:
Inside with: yam
Outside with: yam
Inside func: sam
Outside func: sam
'''
根据你的描述,我觉得你需要的是一个连接池,而不是对象池。为了简单的线程安全,可以把可重用的连接放在一个 Queue.Queue
实例里,叫它 pool
。当一个线程需要创建一个连接的对象时,这个对象就通过 pool.get()
来获取连接(如果当前没有可用的连接,它会自动排队等候,等到有连接可用时再取出来);当这个对象用完连接后,就通过 pool.put
把连接放回池里。
其实,这里除了 Queue.Queue
本身提供的功能之外,几乎没有什么通用的功能,所以没有哪个模块特别出名或受欢迎也就不奇怪了。毕竟,如果一个模块的功能代码只有大约六行(比如调用用户提供的连接工厂来提前或按需填充队列,最多到某个数量),那一般来说也没什么太大的价值。而且,把标准库模块的底层功能厚厚地包裹起来而没有实质性的附加价值,这在架构上其实是个缺点;-)