错误:无法启动新线程

48 投票
10 回答
170828 浏览
提问于 2025-04-15 16:37

我有一个网站,运行的配置是:

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, ())

那么问题是:发生了什么?

谢谢大家,抱歉我的英语不好。:)

10 个回答

13

我之前遇到过类似的情况,但我的程序需要很多线程来处理大量的连接。

我用这个命令来统计线程的数量:

ps -fLu user | wc -l

结果显示有4098个线程。

我切换到那个用户,查看系统的限制:

sudo -u myuser -s /bin/bash

ulimit -u

得到的回应是4096。

于是,我编辑了这个文件 /etc/security/limits.d/30-myuser.conf,添加了以下内容:

myuser hard nproc 16384

myuser soft nproc 16384

重启了服务,现在它运行着7017个线程。

顺便说一下,我的服务器有32个核心,使用这个配置可以处理18000个同时连接。

14

你启动的线程数量超过了你的系统能处理的范围。每个程序能同时活动的线程数量是有限的。

你的应用程序启动线程的速度比这些线程完成工作的速度要快。如果你需要启动很多线程,建议你采取更有控制的方法,比如使用线程池。

46

“无法启动新线程”的错误几乎肯定是因为你在 Python 程序中已经有太多线程在运行了,由于某种资源限制,系统拒绝了你创建新线程的请求。

你可能需要看看你创建了多少个线程;你能创建的最大线程数是由你的环境决定的,但至少应该能达到几百个。

在这里重新考虑一下你的设计可能是个好主意;既然这个程序是异步运行的,也许你可以使用一个线程池来从其他网站获取资源,而不是每次请求都启动一个新线程。

另一个可以改进的地方是你对 Thread.join 和 Thread.stop 的使用;这可能更好地通过给 HTTPSConnection 的构造函数提供一个超时时间来实现。

撰写回答