ошибка: не может начать новую тему - PullRequest
21 голосов
/ 02 декабря 2009

У меня есть сайт, который работает со следующей конфигурацией:

Django + mod-wsgi + apache

В одном из пользовательских запросов я отправляю другой HTTP-запрос в другой сервис и решаю его с помощью библиотеки httplib из python.

Но иногда этот сервис не получает ответа слишком долго, и время ожидания для 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, ())

И ответ: что случилось?

Спасибо за все, и извините за мой чистый английский. :)

Ответы [ 7 ]

27 голосов
/ 02 декабря 2009

Ошибка "не удается начать новый поток" почти наверняка из-за того, что в вашем процессе python уже запущено слишком много потоков, и из-за какого-то ограничения ресурсов запрос на создание нового потока отказ.

Вы, вероятно, должны посмотреть на количество потоков, которые вы создаете; максимальное число, которое вы сможете создать, будет определяться вашей средой, но оно должно составлять не менее сотен.

Вероятно, было бы неплохо переосмыслить вашу архитектуру здесь; видя, что это все равно работает асинхронно, возможно, вы могли бы использовать пул потоков для извлечения ресурсов с другого сайта вместо того, чтобы всегда запускать поток для каждого запроса.

Еще одним улучшением, которое следует учитывать, является использование Thread.join и Thread.stop; это, вероятно, было бы лучше выполнить, предоставив значение тайм-аута для конструктора HTTPSConnection.

8 голосов
/ 02 декабря 2009

Вы запускаете больше потоков, чем может обработать ваша система. Существует ограничение на количество потоков, которые могут быть активными для одного процесса.

Ваше приложение запускает потоки быстрее, чем потоки работают до завершения. Если вам нужно запустить много потоков, вам нужно сделать это более контролируемым образом, я бы предложил использовать пул потоков.

5 голосов
/ 03 декабря 2009

Я думаю, что лучший способ в вашем случае - установить время ожидания сокета вместо порождения потока:

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

Также вы можете установить глобальное время ожидания по умолчанию с помощью функции socket.setdefaulttimeout().

Обновление : см. Ответы на Есть ли способ уничтожить нить в Python? вопрос (есть несколько довольно информативных), чтобы понять, почему. Thread.__stop() не завершает поток, а устанавливает внутренний флаг так, чтобы он считался уже остановленным.

4 голосов
/ 03 декабря 2009

Я полностью переписал код с 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 голосов
/ 03 декабря 2009

Если вы пытаетесь установить тайм-аут, почему бы вам не использовать urllib2 .

0 голосов
/ 22 марта 2019

Если вы используете ThreadPoolExecutor, проблема может заключаться в том, что ваши max_workers выше, чем потоки, разрешенные вашей ОС.

Кажется, что исполнитель хранит информацию о последних выполненных потоках в таблице процессов, даже если потоки уже выполнены. Это означает, что когда ваше приложение работает в течение длительного времени, в конечном итоге оно зарегистрирует в таблице процессов столько потоков, сколько ThreadPoolExecutor.max_workers

0 голосов
/ 03 марта 2019

в моем случае я добавил этот код перед началом новой темы. Это дает приложению максимальный лимит запущенных потоков, которые будут ждать

while threading.active_count()>150 :
    time.sleep(5)
getting.start()
...