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 :




    © 2024 www.doritique.fr par Robert DORIGNY