错误:无法启动新线程

问题描述 投票:21回答:7

我有一个运行跟随配置的网站:

Django + mod-wsgi + apache

在用户的一个请求中,我向另一个服务发送另一个HTTP请求,并通过python的httplib库解决这个问题。

但有时这个服务得不到太长的时间,httplib的超时不起作用。所以我创建线程,在这个线程中我发送请求到服务,并在20秒后加入它(20秒 - 请求超时)。这是它的工作原理:

class HttpGetTimeOut(threading.Thread):
    def __init__(self,**kwargs):
        self.config = kwargs
        self.resp_data = None
        self.exception = None
        super(HttpGetTimeOut,self).__init__()
    def run(self):

        h = httplib.HTTPSConnection(self.config['server'])
        h.connect()
        sended_data = self.config['sended_data']
        h.putrequest("POST", self.config['path'])
        h.putheader("Content-Length", str(len(sended_data)))
        h.putheader("Content-Type", 'text/xml; charset="utf-8"')
        if 'base_auth' in self.config:
            base64string = base64.encodestring('%s:%s' % self.config['base_auth'])[:-1]
            h.putheader("Authorization", "Basic %s" % base64string)
        h.endheaders()

        try:
            h.send(sended_data)
            self.resp_data = h.getresponse()
        except httplib.HTTPException,e:
            self.exception = e
        except Exception,e:
            self.exception = e

像这样的东西......

并通过此功能使用它:

getting = HttpGetTimeOut(**req_config)
getting.start()
getting.join(COOPERATION_TIMEOUT)
if getting.isAlive(): #maybe need some block
    getting._Thread__stop()
    raise ValueError('Timeout')
else:
    if getting.resp_data:
        r = getting.resp_data
    else:
        if getting.exception:
            raise ValueError('REquest Exception')
        else:
            raise ValueError('Undefined exception')

一切正常,但有时我开始捕捉这个异常:

error: can't start new thread

在启动新线程的行:

getting.start()

追溯的下一步和最后一行是

File "/usr/lib/python2.5/threading.py", line 440, in start
    _start_new_thread(self.__bootstrap, ())

答案是:发生了什么?

感谢所有人,对不起我的纯英语。 :)

python django multithreading mod-wsgi httplib
7个回答
27
投票

“无法启动新线程”错误几乎可以肯定,因为您已经在python进程中运行了太多线程,并且由于某种资源限制,创建新线程的请求被拒绝。

您应该查看您正在创建的线程数;您将能够创建的最大数量将由您的环境决定,但它至少应为数百个。

在这里重新思考你的架构可能是一个好主意;因为这无论如何都是异步运行,也许你可以使用一个线程池从另一个站点获取资源,而不是总是为每个请求启动一个线程。

要考虑的另一个改进是使用Thread.join和Thread.stop;通过为HTTPSConnection的构造函数提供超时值,可能会更好。


8
投票

您启动的线程数多于系统可以处理的线程数。对于一个进程可以处于活动状态的线程数有限制。

您的应用程序启动线程的速度比线程运行完成的速度快。如果您需要启动多个线程,您需要以更加可控的方式执行它,我建议使用线程池。


5
投票

我认为在你的情况下最好的方法是设置套接字超时而不是产生线程:

h = httplib.HTTPSConnection(self.config['server'], 
                            timeout=self.config['timeout'])

您还可以使用socket.setdefaulttimeout()函数设置全局默认超时。

更新:查看Is there any way to kill a Thread in Python?问题的答案(有几个非常有用的信息)来理解原因。 Thread.__stop()不会终止线程,而是设置内部标志,以便它被认为已经停止。


4
投票

我完全将代码从httplib重写为pycurl。

c = pycurl.Curl()
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)
c.setopt(pycurl.CONNECTTIMEOUT, CONNECTION_TIMEOUT)
c.setopt(pycurl.TIMEOUT, COOPERATION_TIMEOUT)
c.setopt(pycurl.NOSIGNAL, 1)
c.setopt(pycurl.POST, 1)
c.setopt(pycurl.SSL_VERIFYHOST, 0)
c.setopt(pycurl.SSL_VERIFYPEER, 0)
c.setopt(pycurl.URL, "https://"+server+path)
c.setopt(pycurl.POSTFIELDS,sended_data)

b = StringIO.StringIO()
c.setopt(pycurl.WRITEFUNCTION, b.write)

c.perform()

类似的东西。

我现在正在测试它。谢谢你们所有人的帮助。


3
投票

如果你试图设置超时,为什么不使用qazxsw poi。


0
投票

就我而言,我在开始一个新线程之前添加了这段代码。它为应用程序提供了运行线程的最大限制

urllib2

0
投票

如果您使用的是ThreadPoolExecutor,则问题可能是您的max_workers高于您的操作系统允许的线程。

似乎执行程序将最后执行的线程的信息保存在进程表中,即使线程已经完成。这意味着当您的应用程序运行了很长时间后,最终它将在进程表中注册与ThreadPoolExecutor.max_workers一样多的线程

© www.soinside.com 2019 - 2024. All rights reserved.