Maison  >  Article  >  développement back-end  >  Développement Python – explication détaillée des processus, des threads et des coroutines

Développement Python – explication détaillée des processus, des threads et des coroutines

零下一度
零下一度original
2017-06-27 09:57:521518parcourir

Qu'est-ce qu'un processus ?

Un programme ne peut pas s'exécuter seul. Ce n'est que lorsque le programme est chargé en mémoire et que le système lui alloue des ressources qu'il peut s'exécuter. Ce programme exécuté est appelé un processus. La différence entre un programme et un processus est qu'un programme est un ensemble d'instructions, qui est un texte de description statique du processus. Un processus est une activité d'exécution du programme et est un concept dynamique.

Qu'est-ce qu'un fil de discussion ?

Thread est la plus petite unité sur laquelle le système d'exploitation peut effectuer la planification des opérations. Il est inclus dans le processus et constitue la véritable unité opérationnelle du processus. Un thread fait référence à un seul flux de contrôle séquentiel dans un processus. Plusieurs threads peuvent s'exécuter simultanément dans un processus et chaque thread exécute différentes tâches en parallèle.

Quelle est la différence entre un processus et un thread ?

Les threads partagent l'espace mémoire et la mémoire du processus est indépendante.

Les threads d'un même processus peuvent communiquer directement entre eux, mais deux processus doivent communiquer entre eux via un agent intermédiaire.

Créer un nouveau thread est très simple. Créer un nouveau processus nécessite de cloner son processus parent.

Un thread peut contrôler et faire fonctionner d'autres threads dans le même processus, mais un processus ne peut faire fonctionner que des processus enfants.

Python GIL (Global Interpreter Lock)

Peu importe le nombre de threads ouverts et le nombre de processeurs, python n'autorise qu'un seul thread en même temps lors de l'exécution.

Module de threading Python

appelle directement

  1. import threading, time
  2. def run_num(num):
  3. """
  4. Définir la fonction à exécuter par le fil
  5. :param num:
  6. :retour:
  7. """
  8. print("en cours d'exécution sur le numéro :%s"%num)
  9. time.sleep(3 )
  10. if __name__ == '__main__':
  11. # Générer une instance de thread t1
  12. t1 = threading.Thread(target=run_num,args=( 1,) )
  13. # Générer une instance de thread t2
  14. t2 = threading. Sujet(target =run_num,args=(2,))
  15.  ​ # Démarrer le fil de discussion t1
  16. t1.start()
  17. # Démarrer le fil de discussion t2
  18. t2 .start()
  19. # Obtenez le nom du fil de discussion
  20. print(t1.getName ())
  21. print(t2.getName())
  22. Sortie :
  23. en cours d'exécution sur le numéro :1
  24. en cours d'exécution sur le numéro :2
  25. Thread-1
  26. Thread-2

Appel hérité

  1. fil d'importation, heure
  2. class MyThread(threading.Thread):
  3. def __init__(self,num ):
  4. threading.Thread.__init__(self)
  5. self.num = num
  6. ​ # Définir la fonction à exécuter par chaque thread Le nom de la fonction doit être exécuté
  7. <.>
    def run(self):
  8. print("en cours d'exécution sur le numéro :%s"%self.num)
  9. time.sleep(3)
  10. if __name__ == '__main__':
  11. t1 = MonThread(1)
  12. t2 = MonThread(2)
  13. t1.start()
  14. t2.start()
  15. Sortie :
  16. exécuté sur le numéro : 1
  17. exécuté sur le numéro :2
Rejoindre et démon

Rejoindre

La fonction de Join est de bloquer le processus principal et ne peut pas exécuter le programme après la jointure.

Dans le cas de multi-threads et de jointures multiples, les méthodes de jointure de chaque thread sont exécutées en séquence. L'exécution du thread précédent est terminée avant que le thread suivant puisse être exécuté.

Lorsqu'il n'y a aucun paramètre, attendez la fin du thread avant d'exécuter les programmes suivants.

Après avoir défini les paramètres, attendez le temps défini par le thread, puis exécutez le processus principal suivant, que le thread se termine ou non.

  1. importer le fil, l'heure
  2. class MyThread(threading.Thread):
  3. def __init__(self,num):
  4.                                                                                         threading.Thread.__init__(self) # Définissez la fonction à exécuter par chaque thread. Le nom de la fonction doit être exécuté
  5. def run(self):
  6. print("fonctionnant sur le numéro :%s
    "%self. num)
  7. time.sleep(3)
  8. print(" fil de discussion :%s
    "%self.num)
  9. si __name__ == '__main__' :
  10. t1 = MonThread(1)
  11. t2 = MonThread(2)
  12. t1.start()
  13. t1.join( )
  14.     t2.start()
  15.     t2.join()
  16. 输出:
  17. fonctionne sur le numéro :1
  18. thread:1
  19. en cours d'exécution sur le numéro:2
  20. thread:2

设置参数效果如下:

  1. if __name__ == '__main__':
  2.     t1 = MonThread(1)
  3.     t2 = MonThread(2)
  4.     t1.start()
  5.     t1.join(2)
  6.     t2.start()
  7.     t2.join()
  8. 输出:
  9. en cours d'exécution sur le numéro :1
  10. en cours d'exécution sur le numéro :2
  11. fil :1
  12. fil :2

Démon

有子线程的结束。如果希望主线程不等待子线程,而是在退出时自动Il s'agit d'un démon (démon). 🎜>

  1. temps d'importation,threading
  2. def run(n):
  3.     print("%s".center(20,"*")%n)
  4.     time.sleep(2)
  5.     print("done".center(20,"*" ))
  6. def main() :
  7.     pour i dans plage(5) :
  8.         t = threading.Thread(target =run,args=(i,))
  9.         t.start()
  10.         t.join(1)
  11.         print("fil de discussion",t.getName())
  12. m = threading.Thread(target=main,args=())
  13. # 将main线程设置位Daemon线程,它作为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完成
  14. m.setDaemon(True)
  15. m.start()
  16. m.join(3)
  17. print("thème principal terminé". center(20,"*"))
  18. Sortie :
  19. ************0************
  20. démarrage du fil de discussion- 2
  21. ************1**********
  22. ********terminé************
  23. démarrage du fil de discussion -3
  24. *********2************
  25. **thread principal terminé **
  26. Thread lock (Mutex)

    Plusieurs threads peuvent être démarrés sous un seul et plusieurs threads partagent l'espace mémoire du processus parent. Cela signifie que chaque thread peut accéder aux mêmes données. À ce stade, si deux threads souhaitent modifier les mêmes données en même temps, un verrouillage de thread est requis.

    1. temps d'importation, threading
    2. def addNum():
    3. # Obtenez cette variable globale dans chaque fil
    4. numéro global
    5. print("--get num:",num)
    6. time.sleep(1)
    7. # Effectuer une opération -1 sur cette variable publique
    8. num -= 1
    9. # Définir une variable partagée
    10. num = 100
    11. thread_list = []
    12. pour je dans plage(100) :
    13. t = threading.Thread(target=addNum)
    14. t.start()
    15. thread_list.append(t)
    16. # Attendez que tous les threads terminent leur exécution
    17. for t in thread_list :
    18. t.join()
    19. print("final num:",num)

    Version verrouillée

    Le verrouillage empêche les autres threads de partager l'accès aux ressources, et le même thread ne peut acquérir qu'une seule fois. S'il y en a plusieurs, un blocage se produira et le programme ne pourra pas continuer à s'exécuter.

    1. temps d'importation, threading
    2. def addNum():
    3. # Obtenez cette variable globale dans chaque fil
    4. numéro global
    5. print("--get num:",num)
    6. time.sleep(1)
    7. # Verrouiller avant de modifier les données
    8. lock.acquire()
    9. # Effectuer une opération -1 sur cette variable publique
    10. num -= 1
    11. # Libération après modification
    12. lock.release()
    13. # Définir une variable partagée
    14. num = 100
    15. thread_list = []
    16. # Générer un verrou global
    17. lock = threading.Lock()
    18. pour je dans plage ( 100) :
    19. t = threading.Thread(target=addNum)
    20. t .start()
    21. thread_list.append(t)
    22. # Attendez tout Exécution du fil terminée
    23. pour t dans thread_list :
    24. t.join()
    25. print("numéro final : ",num)

    GIL VS Lock

    GIL garantit qu'un seul thread peut s'exécuter en même temps. Le verrouillage est un verrouillage au niveau de l'utilisateur et n'a rien à voir avec GIL.

    RLock (verrouillage récursif)

    Rlock permet de multiples acquisitions dans un même thread La libération des ressources partagées par un thread nécessite la libération de tous les verrous. Autrement dit, n fois d’acquisition nécessitent n fois de libération.

    1. def run1():
    2. print("récupérez les données de la première partie")
    3. lock.acquire()
    4. num global
    5. num += 1
    6. verrouillage .release()
    7. return num
    8. def run2():
    9. print("récupérez les données de la deuxième partie")
    10. lock.acquire()
    11. global num2
    12. num2 += 1
    13. lock.release()
    14.  retour num2
    15. def run3() :
    16.      lock.acquire()
    17.     res = run1()
    18.     imprimer ("entre run1 et run2".center(50,"*"))
    19.     res2 = run2 ()
    20.     lock.release()
    21.     imprimer (res,res2)
    22. if __name__ == '__main__' :
    23.     num,num2 = 0,0
    24.     lock = threading.RLock()
    25.     pour i dans plage (10) :
    26.         t = threading.Thread(target=run3)
    27.         t.start()
    28. pendant threading.active_count() != 1:
    29.     print(threading.active_count())
    30. else:
    31.     print("tous les fils de discussion terminés".center(50,"*"))
    32.     print(num,num2)

    这两种锁的主要区别是,RLock允许在同一线程中被多次acquire。而Lock Il s'agit d'un système RLock qui permet d'acquérir une version et d'une version n.能真正释放所占用的锁。

    Sémaphore (信号量)

    互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据,比如售票处有3个窗口,那最多只允许3个人同时买票, 后面 的 人 只 能 等 前面 任意 窗口 的 人 离开 才 能 买 票。。

    1. Importer du threading, du temps
    2. def run(n):
    3.     semaphore.acquire()
    4.     time.sleep(1)
    5.     print("lancer le fil :%s"%n)
    6.     semaphore.release()
    7. if __name__ == '__main__':
    8.     # 最多允许5个线程同时运行
    9.     sémaphore = threading.BoundedSemaphore(5)
    10.     pour je dans plage(20):
    11. t = threading.Thread(target=run,args=(i,))
    12. t.start ()
    13. while threading.active_count() != 1 :
    14. # print(threading.active_count())
    15. passe
    16. else:
    17. print("tous les fils de discussion sont terminés ".center(50,"*"))

    Timer (timer)

    Timer appelle une fonction à un certain intervalle , si vous souhaitez appeler une fonction de temps en temps, vous devez redéfinir le Timer dans la fonction appelée par le Timer. Timer est une classe dérivée de Thread.

    1. importer du thread
    2. def bonjour( ):
    3. print("bonjour tout le monde !")
    4. # Exécuter la fonction bonjour après un délai de 5 secondes
    5. t = threading.Timer(5,hello)
    6. t.start()

    Event

    Python fournit l'objet Event pour la communication inter-thread, qui a des paramètres de thread Si l'indicateur de signal est faux, le thread attend que le signal d'instruction soit défini sur vrai par d'autres threads. L'objet Event implémente un mécanisme de communication de thread simple. Il fournit des signaux de configuration, d'effacement, d'attente, etc. pour réaliser la communication entre les threads.

    1. Définir le signal

    Utilisez la méthode set() de Event pour définir l'indicateur de signal à l'intérieur de l'objet Event sur true. L'objet Event fournit la méthode isSet() pour déterminer l'état de son indicateur de signal interne. Lorsque la méthode set() de l'objet événement est utilisée, la méthode isSet() renvoie true.

    1. Effacer le signal

    Utilisez la méthode clear() de Event pour effacer l'indicateur de signal à l'intérieur de l'objet Event, c'est-à-dire le définir sur false Lors de l'utilisation de la méthode clear() d'After Event, la méthode isSet() renvoie false.

    1. Waiting

    La méthode wait() de l'événement ne s'exécutera rapidement et terminera le retour que lorsque le signal interne est vrai. Lorsque l'indicateur de signal interne de l'objet Event est faux, la méthode wait() attend qu'il soit vrai avant de revenir.

    Utilisez Event pour réaliser l'interaction entre deux ou plusieurs threads. Prenons le feu de signalisation comme exemple, démarrez un fil pour agir comme un feu de circulation, générez plusieurs threads pour agir comme des véhicules, et le. les véhicules s'arrêteront selon la règle rouge et verte.

    1. importer le thread, le temps, aléatoire
    2. def light():
    3. if pas event.isSet():
    4.  événement.set()
    5. compte = 0
    6.  pendant Vrai :
    7.  si compte < 5 :
    8. print("
    9.         nombre d'elif < 8 :
    10.             print(" 33[43;1m--lumière jaune allumée-- 33[0m".center(50, "*"))
    11.         elif count < 13 :
    12.             ifévénement.isSet():
    13.                 event.clear()
    14.             print(" 33[41;1m--rouge lumière allumée-- 33[0m".center(50,"*"))
    15.         else :
    16.             count = 0
    17.             événement. set()
    18.         time.sleep(1)
    19.         compte += 1
    20. voiture def( n):
    21.     pendant 1 :
    22.         heure. sleep(random.randrange(10))
    23.         ifevent.isSet():
    24.             print("la voiture %s roule..."%n)
    25.         else:
    26.             print("la voiture %s attend le feu rouge... "%n)
    27. if __name__ == "__main__":
    28.     event = threading.Event()
    29.     Light = threading.Thread(target=light,)
    30.     Light.start()
    31.     pour i dans plage(3) :
    32.         t = threading.Thread(target=car,args=(i,))
    33.         t.start()

    file d'attente

    La file d'attente en Python est la forme la plus couramment utilisée pour échanger des données entre les threads. Le module Queue est un module qui fournit des opérations de file d'attente.

    Créer un objet de file d'attente

    1. file d'attente d'importation
    2. q = queue.Queue(maxsize = 10)

    La classe queue.Queue est une implémentation synchrone d'une file d'attente. La longueur de la file d'attente peut être illimitée ou limitée. La longueur de la file d'attente peut être définie via le paramètre facultatif maxsize du constructeur Queue. Si maxsize est inférieur à 1, la longueur de la file d'attente est illimitée.

    Mettre une valeur dans la file d'attente

    1. q.put("a")

    Appelez la méthode put() de l'objet file d'attente pour insérer un élément à la fin de la file d'attente. put() a deux paramètres : le premier élément est obligatoire et est la valeur de l'élément inséré ; le deuxième bloc est un paramètre facultatif et sa valeur par défaut est 1. Si la file d'attente est actuellement vide et que le bloc vaut 1, la méthode put() provoque la pause du thread appelant jusqu'à ce qu'une unité de données devienne disponible. Si block vaut 0, la méthode put() lèvera une exception Full.

    Supprimer une valeur de la file d'attente

    1. q.get()

    L'appel de la méthode get() de l'objet file d'attente supprime et renvoie un élément de la tête de la file d'attente. Le paramètre facultatif est block, dont la valeur par défaut est True. Si la file d'attente est vide et que block est True, get() provoque la pause du thread appelant jusqu'à ce qu'un élément devienne disponible. Si la file d'attente est vide et que le bloc est False, la file d'attente lèvera une exception vide.

    Le module Python Queue comporte trois files d'attente et constructeurs

    1. # Premier entré, premier sorti
    2. class queue.Queue(maxsize=0)
    3. # Premier entré, dernier sorti
    4. class queue.LifoQueue(maxsize=0)
    5. # Plus la file d'attente prioritaire est basse niveau, le premier il sort
    6. class queue.PriorityQueue(maxsize=0)

    Méthodes courantes

    1. q = queue.Queue()
    2. # Retour la taille de la file d'attente
    3. q.qsize()
    4. # Si la file d'attente est vide, renvoie True, sinon False
    5. q.empty()
    6. # Si la file d'attente est pleine, renvoie True, sinon False
    7. q.full()
    8. # Récupérer la file d'attente, délai d'attente
    9. q.get([block[,timeout]])
    10. # Équivalent à q.get(False)
    11. q.get_nowait ()
    12. # Attendez que la file d'attente soit vide avant d'effectuer d'autres opérations
    13. q .join()

    Modèle producteur-consommateur

    L'utilisation des modèles de producteur et de consommateur dans le développement et la programmation peut résoudre la plupart des problèmes de concurrence. Ce mode améliore la vitesse globale de traitement des données du programme en équilibrant les capacités de travail du thread de production et du thread consommateur.

    Pourquoi utiliser les modèles de producteur et de consommateur

    Dans le monde des threads, le producteur est le fil qui produit des données, et le consommateur est le fil qui consomme des données. Dans le développement multithread, si la vitesse de traitement du producteur est très rapide et celle du consommateur est très lente, alors le producteur doit attendre que le consommateur ait terminé le traitement avant de continuer à produire des données. De la même manière, si la puissance de traitement du consommateur est supérieure à celle du producteur, alors le consommateur doit attendre le producteur. Afin de résoudre ce problème, les modèles producteur et consommateur ont été introduits.

    Qu'est-ce que le modèle producteur-consommateur ?

    Le modèle producteur-consommateur utilise un conteneur pour résoudre le problème du couplage fort entre producteurs et consommateurs. Les producteurs et les consommateurs ne communiquent pas directement entre eux, mais via des files d'attente de blocage. Par conséquent, une fois que le producteur a produit les données, il n'attend plus que le consommateur les traite, mais les jette directement dans la file d'attente de blocage. demandez des données au producteur, mais prenez-les directement à partir de la file d'attente de blocage. La file d'attente de blocage est équivalente à un tampon, équilibrant les capacités de traitement des producteurs et des consommateurs.

    L'exemple le plus basique du modèle producteur-consommateur.

    1. file d'attente d'importation, threading, heure
    2. q = queue.Queue(maxsize=10)
    3. def Producer() :
    4. compte = 1
    5. pendant Vrai :
    6. q.put("Os %s"%count)
    7. print( "Os produit",count)
    8. count += 1
    9. def Consommateur(nom):
    10.  pendant q.qsize ( ) > 0 :
    11. print("[%s] a obtenu [%s] et l'a mangé... "%( nom,q.get()))
    12. time.sleep(1)
    13. p = threading.Thread(target=Producteur,)
    14. c1 = threading.Thread(target=Consumer,args=("Wangcai",))
    15. c2 = threading.Thread (target=Consumer ,args=("来福",))
    16. p.start()
    17. c1.start()
    18. c2.start()

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
Article précédent:Résumé du didacticiel de base de DjangoArticle suivant:Résumé du didacticiel de base de Django

Articles Liés

Voir plus