Maison  >  Article  >  Pool de threads Python et ses principes et utilisations

Pool de threads Python et ses principes et utilisations

zbt
zbtoriginal
2023-06-20 16:44:251668parcourir

Le coût du démarrage d'un nouveau thread par le système est relativement élevé car il implique une interaction avec le système d'exploitation. Dans ce cas, l'utilisation d'un pool de threads peut améliorer considérablement les performances, en particulier lorsque le programme doit créer un grand nombre de threads avec des durées de vie courtes, vous devriez envisager d'utiliser le pool de threads. Le pool de threads crée un grand nombre de threads inactifs au démarrage du système. Tant que le programme soumet une fonction au pool de threads, le pool de threads démarrera un thread inactif pour l'exécuter. Lorsque la fonction est exécutée, le thread ne mourra pas, mais reviendra à nouveau dans le pool de threads et deviendra inactif en attendant d'exécuter la fonction suivante.

Pool de threads Python et ses principes et utilisations

Le coût du démarrage d'un nouveau thread par le système est relativement élevé car il implique une interaction avec le système d'exploitation. Dans ce cas, l'utilisation d'un pool de threads peut améliorer considérablement les performances, en particulier lorsque le programme doit créer un grand nombre de threads avec des durées de vie courtes, vous devriez envisager d'utiliser le pool de threads.

Le pool de threads crée un grand nombre de threads inactifs au démarrage du système. Tant que le programme soumet une fonction au pool de threads, le pool de threads démarrera un thread inactif pour l'exécuter. Lorsque l'exécution de la fonction se termine, le thread ne mourra pas, mais reviendra à nouveau dans le pool de threads et deviendra inactif, en attendant l'exécution de la fonction suivante.

De plus, l'utilisation d'un pool de threads peut contrôler efficacement le nombre de threads simultanés dans le système. Lorsque le système contient un grand nombre de threads simultanés, cela entraînera une forte baisse des performances du système et même l'échec de Python. L'interpréteur plante et le paramètre de nombre maximum de threads du pool de threads peut contrôler le nombre de threads simultanés dans le système pour ne pas dépasser ce nombre.

Utilisation du pool de threads

La classe de base du pool de threads est Executor dans le module concurrent.futures fournit deux sous-classes, à savoir. ThreadPoolExecutor et ProcessPoolExecutor, où ThreadPoolExecutor est utilisé pour créer un pool de threads, et ProcessPoolExecutor est utilisé pour créer un pool de processus.

Si vous utilisez un pool de threads/pool de processus pour gérer la programmation simultanée, soumettez simplement la fonction de tâche correspondante au pool de threads/pool de processus, et le pool de threads/pool de processus s'occupera du reste .

Exectuor fournit les méthodes courantes suivantes :

submit(fn, *args, **kwargs) : Soumettez la fonction fn au pool de threads. *args représente les paramètres passés à la fonction fn, *kwargs Indique que les paramètres sont transmis à la fonction fn sous la forme d'arguments de mots clés.

map(func, *iterables, timeout=None, chunksize=1) : Cette fonction est similaire à la fonction globale map(func, *iterables), mais cette fonction démarrera plusieurs threads pour effectuer immédiatement un traitement de carte sur les itérables de manière asynchrone.

shutdown(wait=True) : fermez le pool de threads.

Une fois que le programme a soumis la fonction de tâche au pool de threads, la méthode de soumission renverra un objet Future. La classe est principalement utilisée pour obtenir la valeur de retour de la fonction de tâche de thread. Puisque la tâche du thread sera exécutée de manière asynchrone dans le nouveau thread, la fonction exécutée par le thread est équivalente à une tâche « à terminer dans le futur », donc Python utilise Un avenir à représenter.

En fait, il y a aussi Future dans la programmation multithread de Java. Le Future ici est similaire au Future de Java.

Future fournit les méthodes suivantes :

cancel() : Annule la tâche de fil représentée par ce Future. Si la tâche est en cours d'exécution et ne peut pas être annulée, la méthode renvoie False sinon, le programme annule la tâche et renvoie Vrai.

cancelled() : indique si la tâche de thread représentée par Future est annulée avec succès.

running() : Si la tâche de thread représentée par le Future est en cours d'exécution et ne peut pas être annulée, cette méthode renvoie True.

done() : Si la tâche de thread représentée par Funture est annulée ou exécutée avec succès, cette méthode renvoie True.

result(timeout=None) : Obtenez le dernier résultat renvoyé par la tâche de thread représentée par ce Future. Si futur Si la tâche de thread représentée n'est pas terminée, cette méthode bloquera le thread actuel, où le paramètre timeout spécifie le nombre maximum de secondes à bloquer.

exception(timeout=None) : récupère l'exception provoquée par la tâche de thread représentée par ce Future. Si la tâche se termine avec succès sans exception, la méthode renvoie Aucun.

add_done_callback(fn) : Enregistrez une "fonction de rappel" pour la tâche de thread représentée par le Future Lorsque la tâche est terminée avec succès, le programme déclenchera automatiquement le fn. fonction.

Après avoir utilisé un pool de threads, la méthode shutdown() du pool de threads doit être appelée, ce qui démarrera la séquence d'arrêt du pool de threads. appeler l'arrêt() Le pool de threads après la méthode ne recevra plus de nouvelles tâches, mais terminera toutes les tâches précédemment soumises. Lorsque toutes les tâches du pool de threads ont été exécutées, tous les threads du pool de threads mourront.

Les étapes pour utiliser un pool de threads pour effectuer des tâches de thread sont les suivantes :

Appelez le constructeur de la classe ThreadPoolExecutor pour créer un pool de threads.

Définissez une fonction normale en tant que tâche de thread.

Appelez la méthode submit() de l'objet ThreadPoolExecutor pour soumettre la tâche de thread.

Lorsque vous ne souhaitez soumettre aucune tâche, appelez la méthode shutdown() de l'objet ThreadPoolExecutor pour arrêter le pool de threads.

Le programme suivant montre comment utiliser le pool de threads pour effectuer des tâches de thread :

from concurrent.futures import ThreadPoolExecutor
import threading
import time
# 定义一个准备作为线程任务的函数
def action(max):
my_sum = 0
for i in range(max):
print(threading.current_thread().name + ' ' + str(i))
my_sum += i
return my_sum
# 创建一个包含2条线程的线程池
pool = ThreadPoolExecutor(max_workers=2)
# 向线程池提交一个task, 50会作为action()函数的参数
future1 = pool.submit(action, 50)
# 向线程池再提交一个task, 100会作为action()函数的参数
future2 = pool.submit(action, 100)
# 判断future1代表的任务是否结束
print(future1.done())
time.sleep(3)
# 判断future2代表的任务是否结束
print(future2.done())
# 查看future1代表的任务返回的结果
print(future1.result())
# 查看future2代表的任务返回的结果
print(future2.result())
# 关闭线程池
pool.shutdown()

Dans le programme ci-dessus, la 13ème ligne de code crée un pool de threads contenant deux threads. Dans les deux lignes de code suivantes, modifiez simplement action(). La fonction est soumise au pool de threads et le pool de threads est responsable du démarrage du thread pour exécuter la fonction action(). Cette méthode de démarrage de threads est à la fois élégante et plus efficace.

Lorsque le programme soumet la fonction action() au pool de threads, la méthode submit() renverra l'objet Future correspondant à la tâche, et le programme détermine immédiatement le futur. done(), qui renverra False (indiquant que la tâche n'est pas terminée à ce moment). Ensuite, le programme principal fait une pause de 3 secondes, puis détermine le done() de future2 méthode, si la tâche est terminée à ce moment-là, cette méthode renverra True.

Le programme obtient enfin les résultats renvoyés par les deux tâches asynchrones grâce à la méthode result() de Future.

Les lecteurs peuvent exécuter eux-mêmes ce code pour voir les résultats, qui ne seront pas démontrés ici.

Lorsque le programme utilise la méthode result() de Future pour obtenir le résultat, cette méthode bloquera le thread actuel si aucun délai d'attente n'est spécifié. paramètres, le thread actuel restera bloqué jusqu'au retour de la tâche représentée par Future.

Obtenir les résultats de l'exécution

Le programme précédent appelait la méthode result() de Future pour obtenir la valeur de retour de la tâche de thread, mais cette méthode bloquera le thread principal actuel seulement une fois la tâche de traitement monétaire terminée, result(). Le blocage de la méthode sera levé.

Si le programme ne souhaite pas appeler directement la méthode result() pour bloquer le thread, il peut utiliser add_done_callback() de Future Méthode pour ajouter une fonction de rappel, la fonction de rappel ressemble à fn(futur). Lorsque la tâche de thread est terminée, le programme déclenchera automatiquement la fonction de rappel et transférera le Future correspondant L'objet est passé en paramètre à la fonction de rappel.

Le programme suivant utilise la méthode add_done_callback() pour obtenir la valeur de retour de la tâche de thread :

from concurrent.futures import ThreadPoolExecutor
import threading
import time
# 定义一个准备作为线程任务的函数
def action(max):
my_sum = 0
for i in range(max):
print(threading.current_thread().name + ' ' + str(i))
my_sum += i
return my_sum
# 创建一个包含2条线程的线程池
with ThreadPoolExecutor(max_workers=2) as pool:
# 向线程池提交一个task, 50会作为action()函数的参数
future1 = pool.submit(action, 50)
# 向线程池再提交一个task, 100会作为action()函数的参数
future2 = pool.submit(action, 100)
def get_result(future):
print(future.result())
# 为future1添加线程完成的回调函数
future1.add_done_callback(get_result)
# 为future2添加线程完成的回调函数
future2.add_done_callback(get_result)
print('--------------')

Le programme principal ci-dessus ajoute la même fonction de rappel pour future1 et future2 respectivement. Cette fonction de rappel obtiendra la valeur de retour lorsque le thread. la tâche se termine.

La dernière ligne de code du programme principal imprime une ligne horizontale. Parce que le programme n'appelle pas directement result() de future1 et future2 méthode, ainsi le thread principal ne sera pas bloqué et vous pourrez immédiatement voir les lignes horizontales imprimées par le thread principal de sortie. Ensuite, vous verrez deux nouveaux threads s'exécuter simultanément. Lorsque la tâche de thread est terminée, get_result(). La fonction est déclenchée et génère la valeur de retour de la tâche de thread.

De plus, puisque le pool de threads implémente le protocole de gestion de contexte, le programme peut utiliser avec pour gérer le pool de threads, évitant ainsi d'avoir à fermer manuellement le pool de threads, comme indiqué dans le programme ci-dessus.

De plus, Exectoruor fournit également une carte (func, *iterables, timeout=None, chunksize=1) La fonction de cette méthode est similaire à la fonction globale map(). La différence est que la méthode map() du pool de threads démarrera un thread pour chaque élément des itérables afin d'exécuter func de manière concurrente. fonction. Cette méthode équivaut à démarrer des threads len(iterables) et à collecter les résultats d'exécution de chaque thread.

Par exemple, le programme suivant utilise la méthode map() de l'exécuteur pour démarrer des threads et collecter les valeurs de retour des tâches de thread :

from concurrent.futures import ThreadPoolExecutor
import threading
import time
# 定义一个准备作为线程任务的函数
def action(max):
my_sum = 0
for i in range(max):
print(threading.current_thread().name + ' ' + str(i))
my_sum += i
return my_sum
# 创建一个包含4条线程的线程池
with ThreadPoolExecutor(max_workers=4) as pool:
# 使用线程执行map计算
# 后面元组有3个元素,因此程序启动3条线程来执行action函数
results = pool.map(action, (50, 100, 150))
print('--------------')
for r in results:
print(r)

Le programme ci-dessus utilise la méthode map() pour démarrer 3 threads (le programme le pool de threads contient 4 threads. Si vous continuez à utiliser un pool de threads contenant seulement deux threads, une tâche sera en attente à ce moment-là. Vous devez attendre que l'une des tâches soit terminée avant que le thread ne soit libre avant d'avoir la possibilité de s'exécuter.) , carte() La valeur de retour de la méthode collectera les résultats de retour de chaque tâche de thread.

Exécutez le programme ci-dessus et vous pourrez également voir les résultats de l'exécution simultanée de 3 threads. Enfin, vous pouvez voir les résultats de retour des 3 tâches de thread via les résultats.

Comme le montre le programme ci-dessus, utiliser la méthode map() pour démarrer un thread et collecter les résultats d'exécution du thread présente non seulement l'avantage d'un code simple, mais aussi du fait que le programme exécutera action() simultanément fonction, mais le résultat de l'exécution de la fonction action() collecté à la fin est toujours cohérent avec le résultat des paramètres transmis. Autrement dit, le premier élément des résultats ci-dessus est l'action (50) Le deuxième élément est le résultat de l'action (100) et le troisième élément est le résultat de l'action (150).

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn