Maison > Article > développement back-end > Exemple détaillé de la façon dont Python gère les problèmes de concurrence via les contrats à terme
Cet article présente principalement Python pour traiter les problèmes de concurrence dans le futur. Il est très bon et a une valeur de référence. Les amis dans le besoin peuvent s'y référer
Première introduction au futur
Utilisez le script suivant pour avoir une compréhension préliminaire du futur :
Exemple 1 : Méthode de boucle ordinaire
import os import time import sys import requests POP20_CC = ( "CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR" ).split() BASE_URL = 'http://flupy.org/data/flags' DEST_DIR = 'downloads/' def save_flag(img,filename): path = os.path.join(DEST_DIR,filename) with open(path,'wb') as fp: fp.write(img) def get_flag(cc): url = "{}/{cc}/{cc}.gif".format(BASE_URL,cc=cc.lower()) resp = requests.get(url) return resp.content def show(text): print(text,end=" ") sys.stdout.flush() def download_many(cc_list): for cc in sorted(cc_list): image = get_flag(cc) show(cc) save_flag(image,cc.lower()+".gif") return len(cc_list) def main(download_many): t0 = time.time() count = download_many(POP20_CC) elapsed = time.time()-t0 msg = "\n{} flags downloaded in {:.2f}s" print(msg.format(count,elapsed)) if __name__ == '__main__': main(download_many)
Exemple 2 : Implémenté via la méthode future, nous réutilisons ici une partie du code ci-dessus
from concurrent import futures from flags import save_flag, get_flag, show, main MAX_WORKERS = 20 def download_one(cc): image = get_flag(cc) show(cc) save_flag(image, cc.lower()+".gif") return cc def download_many(cc_list): workers = min(MAX_WORKERS,len(cc_list)) with futures.ThreadPoolExecutor(workers) as executor: res = executor.map(download_one, sorted(cc_list)) return len(list(res)) if __name__ == '__main__': main(download_many)
Exécuté trois fois respectivement, la vitesse moyenne des deux est : 13,67 et 1.59s, vous voyez que la différence est encore très grande.
futur
futur est un composant important du module concurrent.futures et du module asyncio
À partir de python3.4, il existe deux classes nommées Future dans la bibliothèque standard : concurrent.futures.Future et asyncio.Future
. Ces deux classes ont la même fonction : les instances des deux classes Future représentent des choses qui peuvent être complétées. ou pas encore terminé. Fonctions similaires à la classe Deferred dans Twisted et la classe Future dans le framework Tornado
Remarque : Normalement, vous ne devez pas créer de futur vous-même, mais l'instancier par le framework de concurrence (concurrent.futures ou asyncio)
Raison : future représente quelque chose qui finira par arriver, et la seule façon de déterminer que quelque chose se produira est que le temps d'exécution a été planifié, donc seulement lorsque quelque chose est transmis à la sous-classe concurrent.futures.Executor pour traitement, Une instance concurrent.futures.Future sera créée.
Par exemple : le paramètre de la méthode Executor.submit() est un objet appelable. Après avoir appelé cette méthode, l'heure sera planifiée pour l'objet appelable entrant et un
futur
Le code client ne peut pas changer l'état du futur. Le cadre de concurrence changera l'état du futur après la fin du calcul retardé représenté par le futur. Nous ne pouvons pas contrôler la fin du calcul. Les deux futurs ont une méthode .done(). Cette méthode ne bloque pas et la valeur de retour est une valeur booléenne, indiquant si l'objet appelable lié au futur a été exécuté. Le code client ne demande généralement pas si le futur est terminé, mais attendra une notification. Par conséquent, les deux classes Future ont la méthode .add_done_callback(). Cette méthode n'a qu'un seul paramètre et le type est un objet appelable. L'objet appelable spécifié sera appelé après l'exécution du futur. La méthode .result() a la même fonction dans les deux classes Future : renvoyer le résultat de l'objet appelable, ou relancer l'exception levée lors de l'exécution de l'objet appelable. Mais si le futur ne s'arrête pas, le comportement de la méthode result dans les deux classes Future est très différent.from concurrent import futures from flags import save_flag, get_flag, show, main MAX_WORKERS = 20 def download_one(cc): image = get_flag(cc) show(cc) save_flag(image, cc.lower()+".gif") return cc def download_many(cc_list): cc_list = cc_list[:5] with futures.ThreadPoolExecutor(max_workers=3) as executor: to_do = [] for cc in sorted(cc_list): future = executor.submit(download_one,cc) to_do.append(future) msg = "Secheduled for {}:{}" print(msg.format(cc,future)) results = [] for future in futures.as_completed(to_do): res = future.result() msg = "{}result:{!r}" print(msg.format(future,res)) results.append(res) return len(results) if __name__ == '__main__': main(download_many)Le résultat est le suivant : Remarque : le code Python ne peut pas contrôler GIL, toutes les fonctions de la bibliothèque standard qui effectuent des opérations d'E/S bloquantes libéreront le GIL en attendant que le système d'exploitation renvoie les résultats. Exécutez d'autres threads pour l'exécution. C'est précisément pour cette raison que les threads Python peuvent. jouer un rôle dans les applications gourmandes en IOCe qui précède est concurrent.futures pour démarrer le thread, ce qui suit est de démarrer le processus à travers celui-ci
concurrent.futures pour démarrer le processus
concurrent.futures La classe ProcessPoolExecutor distribue le travail à plusieurs processus Python, donc si vous devez effectuer un traitement gourmand en CPU, utilisez ce module pour contourner le GIL et utiliser tout le CPU noyaux. Le principe est qu'un ProcessPoolExecutor crée N interpréteurs Python indépendants, où N est le nombre de cœurs CPU disponibles sur le système. La méthode d'utilisation est la même que la méthode ThreadPoolExecutorCe 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!