Python 2.7下xmlrpclib.ServerProxy的多线程问题

6 投票
2 回答
2136 浏览
提问于 2025-04-18 18:41

我有一个应用程序,它创建了一个 xmlrpclib.ServerProxy 对象,然后把这个对象传给多个线程(也就是处理网页请求的部分),这些线程会同时进行 XML/RPC 调用。在 Python 2.6 版本下,这一切都运行得很好。但在 Python 2.7 版本中,一旦进入多线程环境,就会出现很多错误,比如 ResponseNotReadyCannotSendRequest

# 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连接不会被重复使用,所有与连接相关的数据似乎都保存在不共享的栈变量中。

所以可能的解决方案有:

  1. 为每个线程创建一个ServerProxy对象(这也会隐式地打开一个TCP连接)
  2. 锁定对一个共享的ServerProxy对象的访问
  3. 实现一个请求队列

撰写回答