urllib2和httplib是否线程安全?

20
我正在寻找关于urllib2httplib线程安全性的信息。 官方文档(http://docs.python.org/library/urllib2.htmlhttp://docs.python.org/library/httplib.html)缺乏有关此主题的任何信息;甚至没有提到单词thread...

更新

好的,它们原生不是线程安全的。 如何使它们线程安全或者是否存在一种情况使它们可以线程安全? 我之所以问这个问题,是因为看起来:

  • 在每个线程中使用独立的OpenerDirector
  • 不在线程之间共享HTTP连接

就足以在线程中安全地使用这些库。类似的用法场景被建议在问题urllib2和cookielib线程安全中。

1个回答

42

httpliburllib2不是线程安全的。

urllib2不提供对一个全局(共享)OpenerDirector对象的串行访问,该对象被urllib2.urlopen()使用。

同样地,httplib也不提供对HTTPConnection对象的串行访问(即通过使用线程安全的连接池),因此在线程之间共享HTTPConnection对象是不安全的。

如果需要线程安全性,建议使用httplib2urllib3作为替代方案。

通常,如果一个模块的文档没有提到线程安全性,我会假设它不是线程安全的。您可以查看模块的源代码来进行验证。

在浏览源代码以确定模块是否线程安全时,您可以首先查找来自threadingmultiprocessing模块的线程同步原语的用法,或使用queue.Queue

更新:

这是来自urllib2.py(Python 2.7.2)的相关源代码片段:

_opener = None
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
    global _opener
    if _opener is None:
        _opener = build_opener()
    return _opener.open(url, data, timeout)

def install_opener(opener):
    global _opener
    _opener = opener

当并发线程调用 install_opener()urlopen() 时,会出现明显的竞争条件。

此外,请注意,使用 Request 对象作为 url 参数调用 urlopen() 可能会改变 Request 对象的值(请参阅OpenerDirector.open()的源代码),因此并发调用带有共享的 Request 对象的 urlopen() 不安全。

总的来说,在满足以下条件的情况下,urlopen() 是线程安全的:

  • install_opener() 没有在另一个线程中被调用。
  • 使用非共享的 Request 对象或字符串作为 url 参数。

2
@ire_and_curses:我已经相应地扩展了我的回答。 - Gregg
3
让用户检查库的源代码以确定给定库是否支持多线程的想法对我来说看起来很奇怪。有些库使用同步代码但不支持多线程(如cookielib),而有些库不使用同步代码但是支持多线程,因为它们利用了无锁结构和算法。 - Piotr Dobrogost
6
我同意用户不应该被强制查看一个库的源代码以确定它是否支持线程安全。如果一个库开发时考虑到了线程安全性,那么我认为文档会表明这一点。如果文档没有提到线程安全性,那么我就认为这个库是不支持线程安全的。为了验证我的假设,通常需要查看库的代码。关于无锁数据结构和cookielib,线程安全性是一个复杂的话题,我只提供了在模块内寻找可能表明其支持线程安全的基准。 - Gregg
@PeteAykroyd 如果没有给出版本,仅通过行号引用源代码是无用的。例如,在Python 2.7.2中,urllib2.py的第1116行是一行空白行... - Piotr Dobrogost
如果你不使用 urllib2.urlopen,而是使用 OpenerDirector.open() 并且 不分享请求对象,那么这应该是线程安全的。 - speedplane
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接