Python 2.7下xmlrpclib.ServerProxy的多线程问题
我有一个应用程序,它创建了一个 xmlrpclib.ServerProxy
对象,然后把这个对象传给多个线程(也就是处理网页请求的部分),这些线程会同时进行 XML/RPC 调用。在 Python 2.6 版本下,这一切都运行得很好。但在 Python 2.7 版本中,一旦进入多线程环境,就会出现很多错误,比如 ResponseNotReady
和 CannotSendRequest
。
# This code works well in python 2.6, and breaks in python 2.7.
import xmlrpclib
import thread
proxy = xmlrpclib.ServerProxy("http://localhost:5000/")
def fetch_users():
print proxy.getUsers()
for _ in range(10):
thread.start_new_thread(fetch_users, ())
while(1):
pass
这里的问题是什么?有没有一种线程安全的方法来重复使用这个 ServerProxy 对象呢?
2 个回答
0
大多数代码并不是线程安全的。不过,我不明白为什么在2.6版本中代码能正常工作,而在2.7版本中却会出错。
这里有另一种看待这个问题的方式:
使用了更高级的
threading
模块代理是每个线程独有的,而不是全局的。这种做法更安全,因为这样线程之间不会因为覆盖一个共享的全局对象而互相干扰。
在最后明确地使用
join()
来等待所有线程完成,确保它们都执行完毕。
来源
import xmlrpclib
import threading
def fetch_users():
proxy = xmlrpclib.ServerProxy("http://localhost:5000/")
print proxy.getUsers()
for _ in range(10):
threading.Thread(target=fetch_users, args=()).start()
# wait for all threads to exit
for th in threading.enumerate():
th.join()
5
我们找到了问题的原因:在Python 2.6中,每次调用XML/RPC方法时,都会创建一个新的TCP连接。而在Python 2.7中,每个ServerProxy对象只会打开一个TCP连接,并且会保持这个连接一直打开(前提是服务器支持keep-alive
)。另外,这个类本身不是线程安全的,所以如果同时有多个请求,就可能会互相干扰。
显然,2.6版本在某种程度上是线程安全的,因为TCP连接不会被重复使用,所有与连接相关的数据似乎都保存在不共享的栈变量中。
所以可能的解决方案有:
- 为每个线程创建一个
ServerProxy
对象(这也会隐式地打开一个TCP连接) - 锁定对一个共享的
ServerProxy
对象的访问 - 实现一个请求队列