IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Une connexion à la DB par thread


Sujet :

Python

  1. #1
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut Une connexion à la DB par thread
    Salut à toutes et à tous,

    Dans le cadre d'un projet web, version WSGI, j'ai besoin d'avoir une connexion à la base de données. Il en faudra une par thread, bien évidemment.

    En soi, rien de très compliqué: threading.local et on est en route.
    Le souci, c'est que threading.local ne semble pas être notifié lorsqu'un thread se termine, et ne permet donc pas aux objets d'être recyclés par le gc.

    Dans mons cas, ça signifierait que les connexions à la db resteraient ouverte pendant toute l'exécution du programme. Ce n'est bien entendu pas acceptable.

    Petit exemple de code pour rendre les choses un peu plus concrètes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    >>> import threading
    >>> 
    >>> local = threading.local()
    >>> class MyClass:
    ...     def __init__(self): print("Init'ing")
    ...     def __del__(self): print("Deleting")
    ... 
    >>> def test():
    ...     local.obj = MyClass()
    ... 
    >>> threading.Thread(target=test).start()
    Init'ing
    On peut voir qu'il n'y a pas de "Deleting", ni une seconde après, ni 3 jours après, puisqu'il reste une référence à l'objet dans le threadlocal.

    Sachant que:
    1. je n'ai pas la main sur la création des threads,
    2. l'usage des weakref dans ce cas-ci n'est pas une possibilité,
    comment puis-je m'assurer que les objets seront détruits une fois que le thread se termine ?

  2. #2
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut
    Je sais pas si j'apprend ca a un membre expert mais pour les thread, que je ne connait pas très bien, il existe une système de verrous (Semaphore) avec les méthodes acquire(), release(), locked() dans module thread il doit y avoir l'équivalent dans threading. Mais je crois que le problème est le code de retour.
    Copy de la doc officiel Python 2.5.6
    current_thread()
    currentThread()
    Return the current Thread object, corresponding to the caller’s thread of control. If the caller’s thread of
    control was not created through the threading module, a dummy thread object with limited functionality
    is returned.
    Je ne sais vraiment pas si ca peut t'aider...
    et j'ai trouver ca dans la doc aussi:
    Semaphore Example
    Semaphores are often used to guard resources with limited capacity, for example, a database server. In any
    situation where the size of the resource size is fixed, you should use a bounded semaphore. Before spawning any
    worker threads, your main thread would initialize the semaphore:
    maxconnections = 5
    ...
    pool_sema = BoundedSemaphore(value=maxconnections)
    Once spawned, worker threads call the semaphore’s acquire and release methods when they need to connect to the
    server:
    pool_sema.acquire()
    conn = connectdb()
    ... use connection ...
    conn.close()
    pool_sema.release()
    The use of a bounded semaphore reduces the chance that a programming error which causes the semaphore to be
    released more than it’s acquired will go undetected.
    Escuse moi de t'avoir ennuyer et polluer ta question, je voulais juste t'aider.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  3. #3
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Hello,

    Les sémaphores sont justement quelque chose à laquelle je tente désespérément d'échapper dans ce cruel monde de l'informatique impérative

    Cela dit, lire ton post m'a donné le déclic: le module "atexit".
    Merci donc pour ton intervention

    Ptit exemple de code pour les curieux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> import atexit
    >>> def test3():
    ...     local.b = MyClass()
    ...     def do_exit():
    ...             del local.b
    ...     atexit.register(do_exit)
    ... 
    >>> threading.Thread(target=test3).start()
    Init'ing
     
    Deleting

  4. #4
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Voici ce que donne votre code pour moi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import threading
    from sys import version
     
    if version.startswith('3'):
        raw_input = input
     
    local = threading.local()
     
    class MyClass:
        def __init__(self):
            print("Init'ing")
        def __del__(self):
            print("Deleting")
     
    def test():
        local.b = MyClass()
        print('local.b', local.b)
     
    threading.Thread(target=test).start()
    raw_input()
    print('local.b', local.b)
    Output
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Init'ing
    local.b <__main__.MyClass object at 0xb745f48c>
    Deleting
    
    Traceback (most recent call last):
      File "testrd1.py", line 21, in <module>
        print('local.b', local.b)
    AttributeError: '_thread._local' object has no attribute 'b'
    @+
    Merci d'utiliser le forum pour les questions techniques.

  5. #5
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Ola oui, il se passe des choses que je ne comprends pas bien il me semble...
    Autant pour le coup de l'AttributeError c'est normal, puisque le thread n'est pas le même, mais voyez plutôt ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    >>> import threading
    >>> 
    >>> def test():
    ...     local.a = MyClass()
    ... 
    >>> local = threading.local()
    >>> class MyClass:
    ...     def __init__(self): print("init")
    ...     def __del__(self): print("del")
    ... 
    >>> threading.Thread(target=test).start()
    init
    >>> threading.Thread(target=test).start()
    init
     
    del
    >>> threading.Thread(target=test).start()
    init
     
    del

  6. #6
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Ah, mystère éclairci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import threading
     
    class MyClass:
        def __init__(self):
            print("Init #{0} in {1}".format(id(self), threading.currentThread().name))
     
        def __del__(self):
            print("Del #{0} in {1}".format(id(self), threading.currentThread().name))
     
     
    local = threading.local()
     
    def witness():
        local.a = MyClass()
     
    threading.Thread(target=witness).start()
    threading.Thread(target=witness).start()
    threading.Thread(target=witness).start()
    threading.Thread(target=witness).start()
    Ce qui nous donne (en tout cas sous Mac OS X Snow Leopard):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Init #4300297064 in Thread-1
     Init #4300297424 in Thread-2
     Init #4300297640 in Thread-3
    Del #4300297064 in Thread-3
    Init #4300298432 in Thread-4
    Del #4300297424 in Thread-4
    Del #4300297640 in Dummy-5
     
    Exception AttributeError: "'NoneType' object has no attribute 'currentThread'" in <bound method MyClass.__del__ of <__main__.MyClass instance at 0x1005158c0>> ignored
    Reste plus qu'à trouver un peu de doc pour déterminer sur quel événement le threadlocal lache les variables. À priori, je dirais qu'il vérifie ça à chaque accès, mais ça me parait un peu lourd...


    Quelqu'un a des infos ?

    Ah, et je viens de voir le "Dummy-5", kezako ?
    EDIT: De la doc de python:
    threading.currentThread()¶
    Return the current Thread object, corresponding to the caller’s thread of control. If the caller’s thread of control was not created through the threading module, a dummy thread object with limited functionality is returned.

  7. #7
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Et allez, un ptit dernier pour la route

    Je viens de jeter un œuil au code de threading.local. Curieux, lisez

    Pour résumer, l'objet threadlocal contient un dictionnaire, qui est également lié au thread.

    Quand on tente d'accéder, de définir ou de supprimer un attribut, le threadlocal se fait patcher pour avoir le dictionnaire du thread en cours.

    Donc, à l'appel du premier witness(), deux objets références le dictionnaire qui contient les valeurs:
    1. le thread
    2. le threadlocal

    Quand le thread se termine, il est détruit. Il ne reste donc plus qu'une seule référence au dictionnaire. Quand on accède aux attributs dans un autre thread, le threadlocal est patché pour avoir le dictionnaire du thread en cours.

    L'autre dictionnaire est perdu à ce moment, d'où le gc tardif.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Connexion à une DB en passant par un serveur
    Par Dasson dans le forum JDBC
    Réponses: 2
    Dernier message: 14/05/2008, 09h10
  2. Utiliser une connexion par Modem Rtc
    Par dede92 dans le forum Windows
    Réponses: 1
    Dernier message: 09/01/2008, 14h37
  3. proteger une connexion internet par mot de passe
    Par astrotouf dans le forum Windows XP
    Réponses: 2
    Dernier message: 06/11/2007, 10h22
  4. Réponses: 7
    Dernier message: 07/06/2007, 18h18
  5. Réponses: 1
    Dernier message: 18/04/2007, 20h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo