pycurl/curl未遵循CURLOPT_TIMEOUT选项

6 投票
2 回答
8709 浏览
提问于 2025-04-16 09:08

我有一个多线程的脚本,有时候在连接到服务器时会卡住,但服务器没有发送任何数据。用Netstat查看时,显示有一个连接的tcp套接字。即使我设置了超时,这种情况也会发生。在没有线程的脚本中,超时设置是正常工作的。这是一些示例代码。

def xmlscraper(url):
  htmlpage = StringIO.StringIO()
  rheader = StringIO.StringIO()
  c = pycurl.Curl()
  c.setopt(pycurl.USERAGENT, "user agent string")
  c.setopt(pycurl.CONNECTTIMEOUT, 60)
  c.setopt(pycurl.TIMEOUT, 120)
  c.setopt(pycurl.FOLLOWLOCATION, 1)
  c.setopt(pycurl.WRITEFUNCTION, htmlpage.write)
  c.setopt(pycurl.HEADERFUNCTION, rheader.write)
  c.setopt(pycurl.HTTPHEADER, ['Expect:'])
  c.setopt(pycurl.NOSIGNAL, 1)
  c.setopt(pycurl.URL, url)
  c.setopt(pycurl.HTTPGET, 1)

pycurl.global_init(pycurl.GLOBAL_ALL)
for url in urllist:
    t = threading.Thread(target=xmlscraper, args=(url,))
    t.start()

如果有人能帮忙,我会非常感激!我已经尝试解决这个问题好几个星期了。

补充说明:
这个url列表大约有10个网址。似乎网址的数量并不重要。

补充说明2:
我刚刚测试了下面的代码。我用了一个php脚本,让它睡眠100秒。

import threading
import pycurl
def testf():
    c = pycurl.Curl()
    c.setopt(pycurl.CONNECTTIMEOUT, 3)
    c.setopt(pycurl.TIMEOUT, 6)
    c.setopt(pycurl.NOSIGNAL, 1)
    c.setopt(pycurl.URL, 'http://xxx.xxx.xxx.xxx/test.php')
    c.setopt(pycurl.HTTPGET, 1)
    c.perform()
t = threading.Thread(target=testf)
t.start()
t.join()

在那段代码中,pycurl似乎能正常超时。所以我猜这可能和网址的数量有关?或者是GIL的问题?

补充说明3:
我觉得这可能和libcurl本身有关,因为有时候我检查脚本时,libcurl仍然连接着一个服务器,持续好几个小时。如果pycurl能正常超时,那么套接字应该早就关闭了。

2 个回答

1

在某些情况下,Python 的线程会受到一个叫做全局解释器锁(GIL)的限制。这意味着你启动的线程可能没有超时,是因为它们实际上没有被频繁地运行。

这个 相关的 StackOverflow 问题 可能会给你一些有用的提示:

3

我把你的'edit2'代码改了一下,让它可以同时运行多个线程,在我的电脑上(Ubuntu 10.10,Python 2.6.6)运行得很好。

import threading
import pycurl

def testf():
    c = pycurl.Curl()
    c.setopt(pycurl.CONNECTTIMEOUT, 3)
    c.setopt(pycurl.TIMEOUT, 3)
    c.setopt(pycurl.NOSIGNAL, 1)
    c.setopt(pycurl.URL, 'http://localhost/cgi-bin/foo.py')
    c.setopt(pycurl.HTTPGET, 1)
    c.perform()

for i in range(100):
    t = threading.Thread(target=testf)
    t.start()

我可以启动100个线程,所有线程在3秒后超时(就像我设定的那样)。

我觉得还不需要去怪罪全局解释器锁(GIL)和线程竞争的问题呢 :)

撰写回答