English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Récemment, j'ai appris la concurrence en Python, donc j'ai fait un résumé des processus multiples, des threads multiples, des asynchrones et des coroutines.
一、多线程
Les threads multiples permettent à plusieurs fonctions d'être activées en même temps à l'intérieur d'un processus, de sorte que les opérations de plusieurs fonctions puissent être exécutées en même temps. Même un ordinateur avec un seul CPU peut créer un effet de threads multiples en changeant constamment entre les instructions des différents threads.
Les threads multiples équivalent à un système concurrent. Un système concurrent exécute généralement plusieurs tâches en même temps. Si plusieurs tâches peuvent partager des ressources, en particulier lorsque plusieurs variables sont écrites en même temps, il faut résoudre le problème de synchronisation, par exemple, le système de vente de billets en ligne des trains multiples : deux instructions, une instruction vérifie si les billets sont vendus, et l'autre instruction, plusieurs guichets vendent des billets en même temps, ce qui peut entraîner la vente de billets inexistant.
Dans des situations de concurrence, l'ordre d'exécution des instructions est déterminé par le noyau. À l'intérieur d'un même thread, les instructions sont exécutées dans l'ordre, mais il est difficile de dire quel thread exécutera en premier entre différents threads. Par conséquent, il faut prendre en compte les problèmes de synchronisation des threads. La synchronisation (synchronization) signifie qu'à un certain moment, seule une thread peut accéder à une ressource.
1Module thread
2Module threading
threading.Thread crée un thread.
Pour juger s'il reste des billets et pour vendre des billets, ajouter un verrou mutualisé pour éviter que l'un des threads ne juge qu'il n'y a plus de billets tout en exécutant l'opération de vente.
#! /usr/bin/python #-* coding: utf-8 -* # __author__ ="tyomcat" import threading import time import os def booth(tid): global i global lock while True: lock.acquire() if i!=0: i=i-1 print "Window:", tid, ", Remaining tickets:", i time.sleep(1) else: print "Thread_id", tid, "No more tickets" os._exit(0) lock.release() time.sleep(1) i = 100 lock=threading.Lock() for k in range(10): new_thread = threading.Thread(target=booth, args=(k,)) new_thread.start()
Deuxièmement, les coroutines (également appelées micro-threads, fibers)
Les coroutines, contrairement au décalage de prise en charge des threads, sont un décalage coopératif. Les coroutines sont également mono-threadées, mais elles permettent d'utiliser des appels asynchrones.+Le code non humain écrit en mode callback peut être écrit de manière apparemment synchrone.
1Les coroutines en python peuvent être réalisées par des générateurs (generator).
Il est nécessaire de comprendre solidement les générateurs et le mot-clé yield.
Appeler une fonction python ordinaire, cela signifie généralement d'exécuter le premier ligne de code de la fonction et de s'arrêter à la ligne return, à une exception ou à l'exécution de la fonction (on peut aussi considérer que None est implicitement retourné).
Une fois que la fonction a restitué le contrôle à l'appelant, cela signifie que tout est terminé. Parfois, on peut créer une fonction qui produit une séquence pour « sauvegarder son travail », c'est ce qu'on appelle un générateur (une fonction qui utilise le mot-clé yield).
Il est possible de « produire une séquence » parce que la fonction ne retourne pas comme on pourrait s'y attendre. Le mot-clé return implique que la fonction renvoie le contrôle de l'exécution au point où la fonction a été appelée. En revanche, l'implique que le transfert de contrôle est temporaire et volontaire, et que notre fonction récupérera à nouveau le contrôle à l'avenir.
Regardons l'exemple de producteur/Exemple de consommateur :
#! /usr/bin/python #-* coding: utf-8 -* # __author__ ="tyomcat" import time import sys # Producteur def produce(l): i=0 while 1: if(i < 10: l.append(i) yield(i) i=i+1 time.sleep(1) else: return # Consommateur def consume(l): p = produce(l) while 1: try: p.next() while(len(l) > 0): print(l.pop()) except StopIteration: sys.exit(0) if __name__ == "__main__": l = [] consume(l)
Lorsque le programme atteint produce(yield i), il renvoie un générateur et met en pause l'exécution. Lorsque nous appelons p.next() dans custom, le programme revient à produce(yield i) pour continuer l'exécution. Ensuite, l'élément est ajouté à l, puis nous appelons print(l.pop()), jusqu'à ce que p.next() déclenche une exception StopIteration.
2、Stackless Python
3、module greenlet
Les implémentations basées sur greenlet sont secondes en performance seulement à Stackless Python, environ deux fois plus lentes que Stackless Python, et presque une magnitude plus rapides que d'autres solutions. En réalité, greenlet n'est pas un mécanisme de concurrence véritable, mais une transition entre différents blocs de code d'exécution de fonctions au sein d'une même thread, mettant en œuvre un schéma de 'toi exécutes un moment, je exécutes un moment', et il est nécessaire de spécifier quand et où passer à la transition lors de la transition.
4、module eventlet
三、多进程
1、module subprocess
En Python, via le paquet subprocess, on peut créer un processus fils et exécuter un programme externe.
Lorsque l'on appelle une commande système, le module os est le premier à être considéré. Les commandes os.system() et os.popen() sont utilisées pour effectuer des opérations. Cependant, ces deux commandes sont trop simples et ne peuvent pas accomplir certaines opérations complexes, telles que fournir des entrées à une commande en cours d'exécution ou lire la sortie d'une commande, juger de l'état de exécution de la commande, gérer la concurrence de multiples commandes, etc. À ce moment-là, la commande Popen du module subprocess peut effectuer efficacement les opérations dont nous avons besoin.
>>> import subprocess >>> command_line=raw_input() ping -c 10 www.baidu.com >>> args=shlex.split(command_line) >>> p=subprocess.Popen(args)
Utiliser subprocess.PIPE pour connecter l'entrée et la sortie de plusieurs sous-processus pour former un pipeline (pipe):
import subprocess child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) child2 = subprocess.Popen(["wc"], stdin=child1.stdout, stdout=subprocess.PIPE) out = child2.communicate() print(out)
La méthode communicate() lit les données de stdout et stderr, et les injecte dans stdin.
2、multiprocessing (paquet multiprocessing)
(1)、le paquet multiprocessing est un paquet de gestion de multiprocessus en Python. Comme threading.Thread, il peut utiliser l'objet multiprocessing.Process pour créer un processus.
Un pool de processus (Process Pool) peut créer plusieurs processus.
apply_async(func,args) - Tirer un processus du pool pour exécuter func, args sont les arguments de func. Il retourne un objet AsyncResult, sur lequel vous pouvez appeler la méthode get() pour obtenir le résultat.
close() - Le pool ne crée plus de nouveaux processus
join() - Attendre que tous les processus du pool soient terminés. Il est nécessaire d'appeler la méthode close() du Pool avant de joindre.
#! /usr/bin/env python # -*- coding:utf-8 -*- # __author__ == "tyomcat" # "Mon ordinateur a4of cpu" from multiprocessing import Pool import os, time def long_time_task(name): print 'Exécuter la tâche %s (%s)...' % (name, os.getpid()) start = time.time() time.sleep(3) end = time.time() print 'Tâche %s s'exécute pendant %0.2f secondes.' % (name, (end - start)) if __name__=='__main__': print 'Processus parent %s.' % os.getpid() p = Pool() for i in range(4): p.apply_async(long_time_task, args=(i,)) print 'En attente que tous les sous-processus soient terminés...' p.close() p.join() print 'Tous les sous-processus terminés.'
(2)、multiprocessing partagé des ressources
Par le biais de la mémoire partagée et de l'objet Manager : utiliser un processus en tant que serveur, établir un Manager pour stocker véritablement les ressources.
Les autres processus peuvent accéder au Manager via des paramètres ou en fonction de l'adresse, établit une connexion et opère sur les ressources du serveur.
#! /usr/bin/env python # -*- coding:utf-8 -*- # __author__ == "tyomcat" from multiprocessing import Queue,Pool import multiprocessing,time,random def write(q): for value in ['A','B','C','D']: print "Put %s to Queue!" % value q.put(value) time.sleep(random.random()) def read(q,lock): while True: lock.acquire() if not q.empty(): value=q.get(True) print "Get %s from Queue" % value time.sleep(random.random()) else: break lock.release() if __name__ == "__main__": manager=multiprocessing.Manager() q=manager.Queue() p=Pool() lock=manager.Lock() pw=p.apply_async(write,args=(q,)) pr=p.apply_async(read,args=(q,lock)) p.close() p.join() print print "Toutes les données ont été écrites et lues"
Quatre, asynchrone
Que ce soit un thread ou un processus, ils utilisent tous des systèmes de numérotation synchronisés. Lorsqu'il y a un blocage, la performance diminue considérablement, ce qui ne permet pas de tirer pleinement parti du potentiel du CPU, gaspille l'investissement matériel, et surtout cause la rigidité des modules logiciels, l'étroite couplage, impossible à découper, ce qui n'est pas favorable à l'extension et aux changements futurs.
Que ce soit un processus ou un thread, chaque blocage, chaque passage nécessite de s'engager dans un appel système (system call), d'abord faire fonctionner le programme de planification de l'ordinateur, puis le programme de planification décide de quel processus (thread) doit être exécuté. Lorsque plusieurs threads accèdent à du code mutuellement exclusif, il est nécessaire d'ajouter un verrou.
Actuellement, les serveurs asynchrones populaires sont basés sur le modèle d'événement (par exemple, nginx).
Dans le modèle d'événement asynchrone, les opérations qui entraîneraient un blocage sont transformées en une opération asynchrone, la ligne principale est responsable de lancer cette opération asynchrone et de traiter le résultat de cette opération asynchrone. Comme toutes les opérations bloquantes sont transformées en opérations asynchrone, théoriquement, la plupart du temps de la ligne principale est utilisé pour traiter les tâches de calcul réelles, ce qui réduit le temps de déplacement des threads, donc la performance de ce modèle est généralement bonne.
C'est tout le contenu de cet article, j'espère qu'il sera utile à votre apprentissage, et j'espère que vous soutiendrez également le tutoriel d'alerte.
Déclaration : Le contenu de cet article est tiré du réseau, propriété de l'auteur original, le contenu est contribué et téléchargé par les utilisateurs d'Internet, ce site ne détient pas de propriété, n'a pas été traité par l'éditeur humain et n'assume pas de responsabilité juridique. Si vous trouvez du contenu suspect de violation de copyright, veuillez envoyer un e-mail à : notice#w3Déclaration : Le contenu de cet article est tiré du réseau, propriété de l'auteur original, le contenu est contribué et téléchargé par les utilisateurs d'Internet, ce site ne détient pas de propriété, n'a pas été traité par l'éditeur humain et n'assume pas de responsabilité juridique. Si vous trouvez du contenu suspect de violation de copyright, veuillez envoyer un e-mail à : notice#w