Python : Programmation multi-thread

15 aout 2014 rdorigny 0 commentaires

Avec le chapitre sur la programmation réseau en Python, j'avais introduis les processus légers (thread). Nous allons approfondir ce sujet car il présente un intérêt évident.

La programmation multi-thread est intéressante pour plusieurs raisons. Elle permet tout d'abord de paralléliser des actions et donc d'accélérer des traitements.

L'autre intérêt de ce type de programmation est dans la capacité offerte au programme principale de transmettre un traitement dangereux (accés à un fichier, accès au réseau, ...) à un autre processus. Ainsi, si le traitement plante, il ne fera pas planter le programme principale, et nous pourrons traiter l'incident. D'ailleurs, cette idée de transmettre un traitement dit dangereux à un autre processus est une des base de la programmation d'une application Android.


1)Le module thread

Pas grand chose à dire sur la librairie thread, l'exemple suivant est suffisant:
#!/usr/bin/python import thread import time # Define a function for the thread def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print "%s: %s" % ( threadName, time.ctime(time.time()) ) # Create two threads as follows try: thread.start_new_thread( print_time, ("Thread-1", 2, ) ) thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print "Error: unable to start thread" while 1: pass

Thread est un module assez ancien, je vous conseille d'utiliser plutôt le module threading.

2)Le module threading

2.1)Comment procéder?

La procédure est toujours la même:
  • 1) On créé une sous-classe qui hérite de la classe threading,
  • 2) On surcharge le constructeur __init__(self [,args]) pour ajouter les attributs spécifiques à votre classe,
  • 3) On surcharge la méthode run(self [,args]) pour implémenter les actions qui devront être réalisées,
  • 4) Il suffit alors de créer une instance de l'objet en invoquant la méthode start().
  • 2.2)Exemple d'utilisation

    #!/usr/bin/python import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print "Starting " + self.name print_time(self.name, self.counter, 5) print "Exiting " + self.name def print_time(threadName, delay, counter): while counter: if exitFlag: thread.exit() time.sleep(delay) print "%s: %s" % (threadName, time.ctime(time.time())) counter -= 1 # Create new threads thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Start new Threads thread1.start() thread2.start() print "Exiting Main Thread"

    Ce qui affichera:

    2.3)Les méthodes de la classe threading

    Voici la liste des principales méthodes proposées par la classe threading:
  • threading.activeCount(): nombre de thread actifs,
  • threading.currentThread(): nombre de thread fils,
  • threading.enumerate(): liste des threads
  • run(): point d'entrée du thread,
  • start(): lance la méthode run()
  • join([time]): Attend que le thread se termine,
  • isAlive(): test si le thread est encore en cours d'execution,
  • getName(): retourne le nom du thread,
  • setName(): implémente un nom au thread.
  • 3)Synchronisation et priorisation

    3.1)La synchronisation par les sémaphores

    Python propose un équivalent aux sémaphores de la programmation C.
    #!/usr/bin/python import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print "Starting " + self.name # entree section critique threadLock.acquire() print_time(self.name, self.counter, 3) # Liberation section critique threadLock.release() def print_time(threadName, delay, counter): while counter: time.sleep(delay) print "%s: %s" % (threadName, time.ctime(time.time())) counter -= 1 threadLock = threading.Lock() threads = [] # Create new threads thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Start new Threads thread1.start() thread2.start() # Add threads to thread list threads.append(thread1) threads.append(thread2) # Wait for all threads to complete for t in threads: t.join() print "Exiting Main Thread"

    Ce qui affichera:

    Autre méthode, python définit une classe sémaphore:
    import threading mysemaphore=threading.Semaphore() mysemaphore.acquire() # code protégé mysemaphore.release() # On peut aussi gérer des accès non bloquant : if mysemaphore.acquire(false): # code protégé mysemaphore.release() else: # ressourse occupé, on ne fait rien.

    3.2)Priorisation avec les queues

    Le module Queue permet de gérer les priorité entre plusieurs threads. Il propose plusieurs méthodes:
  • get(): The get() removes and returns an item from the queue,
  • put(): The put adds item to a queue,
  • qsize() : The qsize() returns the number of items that are currently in the queue,
  • empty(): The empty( ) returns True if queue is empty; otherwise, False,
  • full(): the full() returns True if queue is full; otherwise, False.
  • #!/usr/bin/python import Queue import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, q): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.q = q def run(self): print "Starting " + self.name process_data(self.name, self.q) print "Exiting " + self.name def process_data(threadName, q): while not exitFlag: queueLock.acquire() if not workQueue.empty(): data = q.get() queueLock.release() print "%s processing %s" % (threadName, data) else: queueLock.release() time.sleep(1) threadList = ["Thread-1", "Thread-2", "Thread-3"] nameList = ["One", "Two", "Three", "Four", "Five"] queueLock = threading.Lock() workQueue = Queue.Queue(10) threads = [] threadID = 1 # Create new threads for tName in threadList: thread = myThread(threadID, tName, workQueue) thread.start() threads.append(thread) threadID += 1 # Fill the queue queueLock.acquire() for word in nameList: workQueue.put(word) queueLock.release() # Wait for queue to empty while not workQueue.empty(): pass # Notify threads it's time to exit exitFlag = 1 # Wait for all threads to complete for t in threads: t.join() print "Exiting Main Thread"

    Ce qui affichera:

    Conclusion

    Python propose de nombreuses fonctionnalités autour de ce type de programmation et il ya encore beaucoup à dire (comme les IPC). N'oubliez pas de protéger vos variables entre les theads grâce aux sections critiques. Autre point que j'évoquais au début de l'article, faites réaliser vos actions dangereuses aux threads pour éviter de faire planter votre processus principal.









    Pseudonyme (obligatoire) :
    Adresse mail (obligatoire) :
    Site web :




    © 2017 www.doritique.fr par Robert DORIGNY