1394 votes

Python multithreading pour les nuls

J'essaie de trouver un exemple simple qui montre clairement qu'une tâche unique est divisée pour le multithreading.

Franchement, de nombreux exemples sont trop sophistiqués, ce qui rend le flux plus difficile à utiliser. Quelqu'un voudrait-il partager son échantillon de percée ou un exemple ?

735voto

Alex Martelli Points 330805

Voici un exemple simple : vous devez essayer quelques URL alternatives et renvoyer le contenu de la première qui répond.

import Queue
import threading
import urllib2

# called by each thread
def get_url(q, url):
    q.put(urllib2.urlopen(url).read())

theurls = '''http://google.com http://yahoo.com'''.split()

q = Queue.Queue()

for u in theurls:
    t = threading.Thread(target=get_url, args = (q,u))
    t.daemon = True
    t.start()

s = q.get()
print s

Il s'agit d'un cas où le threading est utilisé comme une simple optimisation : chaque sous-fil attend la résolution et la réponse d'une URL, afin de placer son contenu dans la file d'attente ; chaque fil est un démon (il ne maintiendra pas le processus si le fil principal s'arrête - ce qui est plus fréquent que le contraire) ; le fil principal lance tous les sous-fils, fait un get sur la file d'attente pour attendre que l'un d'entre eux ait effectué un put puis émet les résultats et se termine (ce qui entraîne l'arrêt de tous les sous-filières qui pourraient encore être en cours d'exécution, puisqu'il s'agit de threads de démon).

L'utilisation correcte des threads en Python est invariablement liée aux opérations d'E/S (puisque CPython n'utilise pas de cœurs multiples pour exécuter des tâches liées au CPU de toute façon, la seule raison d'être des threads est de ne pas bloquer le processus pendant l'attente d'une E/S). Les files d'attente sont presque invariablement le meilleur moyen de répartir le travail entre les threads et/ou de collecter les résultats du travail, et elles sont intrinsèquement threadsafe, ce qui vous évite de vous soucier des verrous, des conditions, des événements, des sémaphores et d'autres concepts de coordination/communication inter-threads.

272voto

Michael Aaron Safyan Points 45071

NOTE : Pour une parallélisation réelle en Python, il faut utiliser la fonction multitraitement pour forker plusieurs processus qui s'exécutent en parallèle (en raison du verrouillage global de l'interpréteur, les threads Python fournissent un entrelacement mais sont en fait exécutés en série, et non en parallèle, et ne sont utiles que pour entrelacer les opérations d'E/S).

Cependant, si vous recherchez simplement l'entrelacement (ou si vous effectuez des opérations d'E/S qui peuvent être parallélisées malgré le verrouillage global de l'interpréteur), alors la fonction filetage est l'endroit où commencer. À titre d'exemple vraiment simple, considérons le problème de la sommation d'une grande plage en additionnant des sous-périodes en parallèle :

import threading

class SummingThread(threading.Thread):
     def __init__(self,low,high):
         super(SummingThread, self).__init__()
         self.low=low
         self.high=high
         self.total=0

     def run(self):
         for i in range(self.low,self.high):
             self.total+=i

thread1 = SummingThread(0,500000)
thread2 = SummingThread(500000,1000000)
thread1.start() # This actually causes the thread to run
thread2.start()
thread1.join()  # This waits until the thread has completed
thread2.join()  
# At this point, both threads have completed
result = thread1.total + thread2.total
print result

Notez que l'exemple ci-dessus est très stupide, car il ne fait absolument aucune entrée/sortie et sera exécuté en série bien qu'entrelacé (avec le surcoût du changement de contexte) dans CPython en raison du verrouillage global de l'interpréteur.

107voto

Kay Points 554

Comme d'autres l'ont mentionné, CPython ne peut utiliser les threads que pour I \O les attentes dues à la GIL. Si vous souhaitez bénéficier de plusieurs cœurs pour les tâches liées au CPU, utilisez multitraitement :

from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

93voto

Douglas Adams Points 601

Il est à noter que la file d'attente n'est pas nécessaire pour le threading.

C'est l'exemple le plus simple que j'ai pu imaginer qui montre 10 processus fonctionnant simultanément.

import threading
from random import randint
from time import sleep

def printNumber(number):
   # Sleeps a random 1 to 10 seconds
   sleep(randint(1,10))
   print str(number)

thread_list = []

for i in range(1,10):
   # Instatiates the thread
   # (i) does not make a sequence, so (i,)
   t = threading.Thread(target=printNumber, args=(i,))
   # Sticks the thread in a list so that it remains accessible 
   thread_list.append(t)

# Starts threads
for thread in thread_list:
   thread.start()

# This blocks the calling thread until the thread whose join() method is called is terminated.
# From http://docs.python.org/2/library/threading.html#thread-objects
for thread in thread_list:
   thread.join()

# Demonstrates that the main process waited for threads to complete
print "Done"

49voto

JimJty Points 397

La réponse d'Alex Martelli m'a aidé, mais voici une version modifiée qui m'a semblé plus utile (du moins pour moi).

import Queue
import threading
import urllib2

worker_data = ['http://google.com', 'http://yahoo.com', 'http://bing.com']

#load up a queue with your data, this will handle locking
q = Queue.Queue()
for url in worker_data:
    q.put(url)

#define a worker function
def worker(queue):
    queue_full = True
    while queue_full:
        try:
            #get your data off the queue, and do some work
            url= queue.get(False)
            data = urllib2.urlopen(url).read()
            print len(data)

        except Queue.Empty:
            queue_full = False

#create as many threads as you want
thread_count = 5
for i in range(thread_count):
    t = threading.Thread(target=worker, args = (q,))
    t.start()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X