Le module threading

Pour faire simple, avec ce module vous "fabriquez" des objets de type Thread, chacun ayant une tache a accomplir. Vous lancez ces objets dans la nature et vous attendez qu'ils reviennent...

Comment ça se passe dans la pratique? :reflechi:

Déjà, il vous faut une fonction suffisamment lourde pour mettre à genoux votre machine:

K = 50
 
def CostlyFunction(z):
	result = 0
	for k in xrange(1, K+2):
		result += z ** (1 / k**1.5)
	return result

Honteusement pompée du fil Stack Overflow: multiprocess or threading in python?.

Comme je vous disais, il vous faut un objet de type Thread:

import threading
 
K = 50
 
class CostlyThread(threading.Thread):
 
	def __init__(self, value):
 
		threading.Thread.__init__(self)
		self.value = value
 
	def run(self):
		# notre fonction
		result = 0
		z = self.value
		for k in xrange(1, K+2):
			result += z ** (1 / k**1.5)
		self.value = result

Et on balance tout!

import time
 
# timer
startTime = time.time()
 
# va stocker les threads qu'on va lancer.
threadList = []
 
for i in xrange(100000) :	# on lance cent milles threads
 
	curThread = CostlyThread(i)
	curThread.start()	# on lance le thread
	threadList.append(curThread)	# on ajoute le thread a la list
 
# maintenant qu'on a tout lance, on récupère tout!
resultList = []
 
for curThread in threadList :
 
	curThread.join()	# on attend que le thread ait fini
	result = curThread.value	# on récupère sa valeur
	resultList.append(result)
 
print time.time() - startTime

Sauvez tout ça dans un fichier python puis lancez un moniteur système et regardez bien ce que Python va faire...

21.4 secondes plus tard... :siffle:

threadingVSmultiprocess_002.png

Vous remarquerez que Python n'a pas tous les processeurs à fond. :redface:

En fait, le threading n'a pas pour vocation de faire "beaucoup de petits calculs". Il n'est pas efficace pour ce genre de chose (je ne rentre pas dans les détails, c'est très bien expliqué dans le fil Stack Overflow: Are Python threads buggy?).

Un des principal intérêt du threading c'est sa mise en place. Il est très simple à utiliser. On va principalement s'en servir pour faire des opérations nécessitant de la "latence".

Je pense qu'on doit pouvoir aller plus loin mais personnellement, je l'utilise souvent quand je dois faire plusieurs appels réseaux dans une boucle (récupérer la date de modification d'une liste de fichiers) c'est beaucoup plus efficace que d'attendre a chaque fois que le réseau nous donne la main pour redemander une informations similaire juste après.

Pas convaincu?

25709 existences de fichier vérifiées en 38.35 secondes :IFuckTheWorld:

J'ose même pas imaginer le temps que ça aurait pris si j'avais fait la même chose en récursif...

Après, le réseau en question était solide (c'est pas du routeur de promotion Carrefour! :baffed: ). Cela dit, suivant l’opération faite par le thread vous pouvez plomber le réseau (ce sera toujours moins pire que Nuke et After Effect en rendu cela dit :trollface: )... Donc faite attention.

Viens maintenant le problème de "calcul brut". Si le threading ne permet pas d'utiliser l’intégralité de son processeur, comment puis-je faire des calculs très lourds?

C'est la seconde partie de ce billet! :hehe:

Le module multiprocessing

Ce module est un petit peu plus subtil à utiliser et il y a plusieurs façons de faire.

La méthode que nous allons voir est l'une des (la?) plus efficace:

import multiprocessing as mp
 
# timer
startTime = time.time()
 
pool = mp.Pool()
asyncResult = pool.map_async(CostlyFunction, xrange(100000))	# on lance aussi cent milles operations
resultList = asyncResult.get()	# asyncResult.get() is a list of values
print time.time() - startTime

0.9 secondes plus tard (Et ouai, faut suivre...)

Vous n'avez peut être rien vu mais si vous avez toujours votre moniteur système d'ouvert avec un graph de charge processeur, vous verrez que pendant ce court laps de temps, tous les processeurs étaient en pleine activité...

threadingVSmultiprocess_001.png

Ajoutez un zéro et regardez comment votre processeur il pleure sa maman (on gaspille aussi plein d’énergie hein! :seSentCon: ).

Je m’arrête la. Le module multiprocessing offre beaucoup plus de possibilités (gestion d'une queue). Mais pour l'instant, je n'ai jamais eu a l'utiliser. :pasClasse:

Conclusion

J’espère que ce petit billet vous aura donné des bouts de code suffisant pour débuter. Personnellement, j’apprécie beaucoup la simplicité du module threading qu'on peut sortir dans pas mal de situations des lors qu'on touche au fichiers.

A bientôt!

Dorian

:marioCours: