web-dev-qa-db-fra.com

erreur: impossible de démarrer un nouveau thread

J'ai un site qui fonctionne avec la configuration suivante:

Django + mod-wsgi + Apache

Dans l'une des demandes des utilisateurs, j'envoie une autre demande HTTP à un autre service, et je résous cela par la bibliothèque httplib de python.

Mais parfois, ce service n'obtient pas de réponse trop longtemps et le délai d'attente pour httplib ne fonctionne pas. Donc, je crée un fil, dans ce fil, j'envoie une demande au service et je le rejoins après 20 secondes (20 secondes - c'est un délai d'expiration de la demande). Voilà comment cela fonctionne:

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

quelque chose comme ça...

Et utilisez-le par cette fonction:

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')

Et tout fonctionne bien, mais parfois je commence à attraper cette exception:

error: can't start new thread

à la ligne de démarrage d'un nouveau thread:

getting.start()

et la ligne de traceback suivante et finale est

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

Et la réponse est: que se passe-t-il?

Merci à tous et désolé pour mon anglais pur. :)

22
Oduvan

L'erreur "impossible de démarrer un nouveau thread" est presque certainement due au fait que vous avez déjà trop de threads en cours d'exécution dans votre processus python, et en raison d'une limite de ressources quelconque, la demande pour créer un nouveau thread est refusé.

Vous devriez probablement regarder le nombre de threads que vous créez; le nombre maximum que vous pourrez créer sera déterminé par votre environnement, mais il devrait être de l'ordre de centaines au moins.

Ce serait probablement une bonne idée de repenser votre architecture ici; étant donné que cela s'exécute de manière asynchrone, vous pouvez peut-être utiliser un pool de threads pour extraire des ressources d'un autre site au lieu de toujours démarrer un thread pour chaque demande.

Une autre amélioration à considérer est votre utilisation de Thread.join et Thread.stop; cela serait probablement mieux accompli en fournissant une valeur de délai d'attente au constructeur de HTTPSConnection.

28
Gabriel Reid

Vous démarrez plus de threads que votre système ne peut en gérer. Le nombre de threads pouvant être actifs pour un processus est limité.

Votre application démarre les threads plus rapidement que les threads ne s'exécutent jusqu'à la fin. Si vous devez démarrer de nombreux threads, vous devez le faire de manière plus contrôlée, je suggère d'utiliser un pool de threads.

9
Tendayi Mawushe

Je pense que la meilleure façon dans votre cas est de définir le délai d'expiration du socket au lieu de générer le thread:

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

Vous pouvez également définir un délai global par défaut avec la fonction socket.setdefaulttimeout() .

Mise à jour : Voir les réponses à Existe-t-il un moyen de tuer un Thread en Python? question (il y en a plusieurs assez informatives) pour comprendre pourquoi. Thread.__stop() ne termine pas le thread, mais définit plutôt l'indicateur interne de sorte qu'il soit considéré comme déjà arrêté.

5
Denis Otkidach

Je réécris complètement le code de httplib vers 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()

quelque chose comme ca.

Et je le teste maintenant. Merci à tous pour votre aide.

4
Oduvan

Si vous tentez de définir un délai d'expiration, pourquoi n'utilisez-vous pas rllib2 .

3
piyer

Si vous utilisez un ThreadPoolExecutor, le problème peut être que votre max_workers est supérieur aux threads autorisés par votre système d'exploitation.

Il semble que l'exécuteur conserve les informations des derniers threads exécutés dans la table de processus, même si les threads sont déjà effectués. Cela signifie que lorsque votre application fonctionne depuis longtemps, elle enregistrera éventuellement dans la table de processus autant de threads que ThreadPoolExecutor.max_workers

0
Jose Enrique