2009-01-15 10 views
5

J'ai un code enfilé où chaque thread doit écrire dans le même fichier. Pour éviter les problèmes de concurrence, j'utilise un objet Lock.Accès aux ressources multithread - Où dois-je mettre mes serrures?

Ma question est de savoir si j'utilise le verrou correctement. Si je mets le verrou à l'intérieur de chaque thread, ce verrou est-il global ou seulement spécifique à ce thread spécifique?

Fondamentalement, dois-je créer un verrou d'abord et passe sa référence à chaque fil, ou est-ce ok pour le mettre à l'intérieur du fil comme je le fais ici:

import time 
from threading import Thread, Lock 

def main(): 
    for i in range(20): 
     agent = Agent(i) 
     agent.start() 

class Agent(Thread): 
    def __init__(self, thread_num): 
     Thread.__init__(self) 
     self.thread_num = thread_num 

    def run(self): 
     while True: 
      print 'hello from thread %s' % self.thread_num 
      self.write_result() 

    def write_result(self): 
     lock = Lock() 
     lock.acquire() 
     try: 
      f = open('foo.txt', 'a') 
      f.write('hello from thread %s\n' % self.thread_num) 
      f.flush() 
      f.close() 
     finally: 
      lock.release() 

if __name__ == '__main__': 
    main() 

Répondre

3

Créer le verrou en dehors de la méthode.

class Agent(Thread): 
    mylock = Lock() 
    def write_result(self): 
     self.mylock.acquire() 
     try: 
      ... 
     finally: 
      self.mylock.release() 

ou si vous utilisez python> = 2.5:

class Agent(Thread): 
    mylock = Lock() 
    def write_result(self): 
     with self.mylock: 
      ... 

Pour utiliser cela avec Python 2.5, vous devez importer la déclaration de l'avenir:

from __future__ import with_statement 
+0

Oui, vous l'avez déplacé en dehors de la méthode, mais vous le créez toujours dans le fil lui-même. N'est-ce pas un problème? –

+0

@cgoldberd: il est créé en tant qu'attribut * class *, ce qui signifie qu'un seul sera créé pour tous les threads. C'est un meilleur endroit pour le garder car tout reste dans la classe des threads. – nosklo

+0

gotcha. J'aime cette approche –

1

Le retour de la méthode de verrouillage() un objet de verrouillage pour chaque appel. Donc chaque thread (en fait chaque appel à write_result) aura un objet lock différent. Et il n'y aura pas de verrouillage.

1

Le verrou utilisé doit être commun à tous les threads, ou au moins s'assurer que deux verrous ne peuvent pas verrouiller la même ressource en même temps.

0

Je suis assez sûr que le verrou doit être le même objet pour chaque thread. Essayez ceci:

import time 
from threading import Thread, Lock 

def main(): 
    lock = Lock() 
    for i in range(20): 
     agent = Agent(i, lock) 
     agent.start() 

class Agent(Thread, Lock): 
    def __init__(self, thread_num, lock): 
     Thread.__init__(self) 
     self.thread_num = thread_num 
     self.lock = lock 

    def run(self): 
     while True: 
      print 'hello from thread %s' % self.thread_num 
      self.write_result() 

    def write_result(self): 
     self.lock.acquire() 
     try: 
      f = open('foo.txt', 'a') 
      f.write('hello from thread %s\n' % self.thread_num) 
      f.flush() 
      f.close() 
     finally: 
      lock.release() 

if __name__ == '__main__': 
    main() 
+0

-1: cela ne fonctionnera pas. – nosklo

+0

nosklo. pourquoi ça ne marchera pas? Le verrou est créé en premier, puis transmis dans chaque thread. –

+0

Désolé, j'ai oublié de passer l'objet lock au constructeur de l'agent. Corrigée. –

1

L'instance de verrou doit être associée à l'instance de fichier. En d'autres termes, vous devez créer à la fois le verrou et le fichier en même temps et passer les deux à chaque thread.

6

Pour votre cas d'utilisation d'une approche pourrait consister à écrire une file sous-classe qui bloque:

class LockedWrite(file): 
    """ Wrapper class to a file object that locks writes """ 
    def __init__(self, *args, **kwds): 
     super(LockedWrite, self).__init__(*args, **kwds) 
     self._lock = Lock() 

    def write(self, *args, **kwds): 
     self._lock.acquire() 
     try: 
      super(LockedWrite, self).write(*args, **kwds) 
     finally: 
      self._lock.release() 

Pour utiliser dans votre code il suffit de remplacer les fonctions suivantes:

def main(): 
    f = LockedWrite('foo.txt', 'a') 

    for i in range(20): 
     agent = Agent(i, f) 
     agent.start() 

class Agent(Thread): 
    def __init__(self, thread_num, fileobj): 
     Thread.__init__(self) 
     self.thread_num = thread_num 
     self._file = fileobj  

# ... 

    def write_result(self): 
     self._file.write('hello from thread %s\n' % self.thread_num) 

Cette approche met le verrouillage des fichiers dans le fichier lui-même qui semble plus propre IMHO

+0

Excepté la partie inutile "filelock = Lock()", j'aime cette solution mieux que la mienne. Ce serait encore mieux si cela interceptait d'autres tentatives d'ouvrir foo.txt pour écrire/ajouter et retourner à l'objet LockedWrite original. –

+0

@Joseph: supprimé la partie inutile. – nosklo

+0

Vous avez mon vote. –

1

Vous pouvez simplifier les choses un peu (au prix d'un peu plus frais généraux) en désignant un seul d (créé probablement exclusivement à cet effet) en tant que thread unique qui écrit dans le fichier et tous les autres threads sont délégués à l'éditeur de fichiers en plaçant la chaîne qu'ils souhaitent ajouter au fichier dans un objet queue.Queue.

Les files d'attente ont toutes les fonctions de verrouillage intégrées, de sorte que n'importe quel thread peut appeler en toute sécurité Queue.put() à tout moment. Le fichier-writer serait le seul thread appelant Queue.get(), et peut probablement passer une grande partie de son temps à bloquer sur cet appel (avec un délai raisonnable pour permettre au thread de répondre proprement à une demande d'arrêt). Tous les problèmes de synchronisation seront traités par la file d'attente, et vous aurez à vous préoccuper de savoir si vous avez oublié un verrou acquérir/libérer quelque part ...:)

+0

Je fais normalement ce genre de choses avec des files d'attente et un seul auteur aussi. J'avais juste besoin de quelques précisions sur les serrures. –