程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python + Threading module Multithreading Learning

編輯:Python

python Multithreading

  • Python Threadthreading
    • PythonMultithreading pourI/ODense
    • PythonProcessus de travail multithreadé
    • Constructeur
    • Fonction thread
      • start()
      • join(timeout=None)
    • Créer un thread
    • Verrouillage du fil
    • 5Type de verrouillage du fil
    • joinFonctions
      • Première catégorie:PythonPar défaut Multithread(Fils non gardiens)
      • Deuxième type:Fils de démon
      • La troisième:AdhésionjoinSynchronisation des paramètres de la méthode(Fils non gardiens,EtjoinNe pas définir le délai)
      • Type 4:AdhésionjoinSynchronisation des paramètres de la méthode(Fils non gardiens,joinDéfinir le délai)
      • Cinquième catégorie:AdhésionjoinSynchronisation des paramètres de la méthode(*Fils de démon*,joinDéfinir le délai)

Python Threadthreading

PythonMultithreading pourI/ODense

GILSon nom complet estGlobal Interpreter Lock(Global Interpreter Lock),Pour la sécurité des données,GILAssurez - vous qu'un seul thread reçoit les données en même temps.Alors...,InpythonMoyenne, Un seul thread peut être exécuté en même temps .

EtIODense,Le Multithreading améliore l'efficacité( Il y aIOL'opération aura lieuIOAttendez.,Une perte de temps inutile,Et l'ouverture de multithreads peut être faite dans le threadAEn attente,Passer automatiquement au threadB,Peut ne pas gaspillerCPURessources,Afin d'améliorer l'efficacité de l'exécution du programme ).Alors...pythonPaire multithreadéeIOCode dense plus convivial.

EtCPUDense( Divers traitements cycliques、Calcul, etc ), En raison de la charge de calcul , Le minuteur atteindra bientôt le seuil ,Et puisDéclencheurGILLibération et remise en concurrence de( Passer d'un thread à l'autre nécessite certainement des ressources ),Alors...pythonPaire multithreadéeCPULe Code dense n'est pas convivial.

PythonProcessus de travail multithreadé

PythonLors de l'utilisation de multithreads,AppelécLe fil natif de la langue.

  1. Obtenir des données publiques
  2. ApplicationGIL(Global Interpreter Lock)
  3. pythonAppel de l'interpréteurosFils natifs
  4. osFonctionnementcpuEffectuer des opérations
  5. Quand le temps d'exécution du thread est écoulé , Que l'Opération soit terminée ou non ,GIL On leur a demandé de libérer
  6. Répéter le processus ci - dessus par un autre processus
  7. Une fois les autres processus terminés , Passe à nouveau au fil précédent ( Poursuivre l'exécution à partir du contexte dans lequel il a enregistré ), Tout le processus est que chaque thread effectue ses propres opérations , Basculer lorsque le temps d'exécution est écoulé (context switch).

Notes: Certains ne comprennent pas ?

Constructeur

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

En appelant ce constructeur , Doit avoir un paramètre clé .Les paramètres sont les suivants::

  1. group Ça devrait être None;Pour l'expansion future ThreadGroup Classe implémentée avec maintien .

  2. target C'est pour run() Méthode appeléeObjet appelable(Une fonction).Par défaut None, Indique qu'aucune méthode n'a besoin d'être appelée .

  3. name - Oui.Nom du thread.Par défaut,Par “Thread-N” Le format constitue un nom unique ,Parmi eux N C'est un petit nombre décimal . Plusieurs Threads peuvent avoir le même nom .

  4. args Est utilisé pour appeler la fonction cible Tuples de paramètres.Par défaut ().

  5. kwargs Est utilisé pour appeler la fonction cible Dictionnaire des paramètres clés .Par défaut {}.

  6. daemon Paramètre sinon None, Définit explicitement si le thread est en mode démon . Si oui None (Par défaut),Le thread vaSuccessionDu thread courant Propriétés du mode démon .
    3.3 La version et au - dessus ont cette propriété .
    Attention!:Assurez - vous d'appeler start() C'est réglé ,Sinon, ça va sortir RuntimeError .
    La valeur initiale est héritée du thread create ; Le thread principal n'est pas un thread gardien , Par conséquent, tous les Threads créés par le thread principal sont par défaut daemon = False.
    Quand il n'y a pas de fils non - gardiens vivants ,ToutPythonLe programme va sortir.

  7. Si le Sous - type ** Surcharge du constructeur ,** Il doit s'assurer que Avant de faire quoi que ce soit ,Commencez par Appeler le constructeur de classe de base (Thread.init()).

Interprétation des termes:Mode gardien

Il y a un type de thread,C'est dansEn coulissesDe, Sa mission est de servir d'autres fils ,Ce fil s'appelle“Fil de fond(Daemon Thread)”,Aussi appelé“Fils de démon”Ou“Les fils elfes”.Python Le thread de collecte des ordures de l'interpréteur est un Thread de fond typique .
Le fil de fond a une caractéristique , Si tous les fils de la réception sont morts , Alors le fil de fond meurt automatiquement .

Fonction thread

start()

Démarrer l'activité thread .

Il est là.Un fil C'est le plus grand. Il ne peut être appelé qu'une seule fois. Il arrange les objets run() La méthode est appelée dans un processus de contrôle indépendant .

Si la méthode est appelée dans le même objet thread Plus d'une fois ,Oui.Jetez RuntimeError .

join(timeout=None)

Attendez., Jusqu'à la fin du thread .

C'est vrai.Blocage Le thread qui appelle cette méthode ,Jusqu'à ce qu'il soit appelé join() Fin du fil pour .

  • Que ce soit la fin normale
  • Ou lancer une exception non gérée
  • Ou jusqu'à ce qu'un délai se produise, L'option timeout est facultative .

Quand timeout Paramètres Existe et n'est pas None Heure, Il devrait s'agir d'un délai d'action spécifié pour SecondesEn unitésNombre de points flottants(Ou score).
Parce que join() Toujours revenir None , Vous devez donc être join() AprèsAppelezis_alive() Pour déterminer si un délai s'est produit ,Si le fil est toujours vivant,Et join() Temps mort.

Quand timeout Le paramètre n'existe pas ou None ,Cette opération va Blocage jusqu'à la fin du fil .

Un filPeut être join() Plusieurs fois..

  • Si Essayer d'ajouter le fil courant provoque une impasse , join() Peut causer RuntimeError Anomalie.
  • Si vous essayez join() Un thread qui n'a pas encore commencé , La même exception sera lancée .

Créer un thread

import threading # Module thread
import time
def run(n):
print("task", n)
time.sleep(1)
print('2s')
time.sleep(1)
print('3s')
if __name__ == '__main__':
th = threading.Thread(target=run,name="thread_1" args=("thread 1",), daemon=True) # Créer un thread
# Définir le Sous - processus comme un fil de démon ,**Il doit êtrestart()Réglage précédent
th.setDaemon(True) # Configurer les fils du démon, En fait, au moment de la création C'est réglé. daemon=True
th.start()
# Définir le thread principal pour attendre la fin du sous - thread
th.join()
print("end")

Verrouillage du fil

Parce qu'il y a une programmation aléatoire entre les fils,Et chaque thread ne peut exécuter quenAprès l'exécution de l'article, Cela peut se produire lorsque plusieurs Threads modifient simultanément les mêmes données Données salesNotes:PythonType de données de base、Liste、Tuple、 Les dictionnaires sont sans fil , Il n'y aura donc pas d'écrasement du programme , Mais il en résulte une valeur inconnue pour les données ,Données sales),

Donc il y a un verrou de fil,C'est - à - dire qu'un thread est autorisé à fonctionner en même temps. Le verrouillage par fil est utilisé pour verrouiller les ressources , Plusieurs serrures peuvent être définies ,Comme le code ci - dessous, Quand il faut monopoliser une ressource , N'importe quelle serrure peut verrouiller cette ressource , C'est comme si vous pouviez verrouiller la même porte avec différentes serrures .

Parce que les fils sont programmés au hasard,Si plusieurs Threads manipulent un objet en même temps, Et l'objet n'est pas bien protégé ,Résultats inattendus du programme,C'est pour ça qu'on l'appelle aussi“Thread Unsafe”.
Pour éviter ce qui précède, Et la serrure est apparue .

5Type de verrouillage du fil

  • Verrouillage synchrone
  • Verrouillage récursif
  • Verrouillage conditionnel
  • Verrouillage des événements
  • Verrouillage sémaphore

Je suis5EspècePythonVerrouillage du fil Il est détaillé dans .

joinFonctions


Supplément conceptuel

  • InPythonDans la programmation multithreadée,join La fonction de la méthode est de synchroniser les fils
  • Fils de démon, Être pour protéger les autres , Quand il est réglé sur le fil du démon , Après que le fil principal protégé n'existe pas , Le fil de démon n'existe pas non plus.

Les points suivants 5 Différentes formes d'interprétation join Utilisation dans la programmation multithreadée

Première catégorie:PythonPar défaut Multithread(Fils non gardiens)

Python Par défaut pour Multithreading (Définir le threadsetDaemon(False)),Une fois que le thread principal a terminé sa tâche,J'ai arrêté.,À ce stade, le Sous - thread poursuit sa tâche,Jusqu'à la fin de sa mission

Notes:setDaemon(False) C'est - à - dire: Le thread est défini comme un thread non - démon ; Après la sortie du programme principal , Les sous - fils ne sortent pas automatiquement .

import threading, time
def doWaiting1():
print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(3)
print("Thread1 J'ai reçu l'ordre de couvrir ")
print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(8)
print("Thread2 J'ai reçu l'ordre de couvrir ")
print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")
tsk = [] # Liste des Threads
# Créer et ouvrir des fils1
thread1 = threading.Thread(target = doWaiting1)
thread1.start() # start()Fonctions Appel réel RUN()Fonctions(PythonAppelé CThread for Language)
tsk.append(thread1)
# Créer et ouvrir des fils2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)
# Programme de chronométrage 
print('start join: ' + time.strftime('%H:%M:%S') )
print('end join: ' + time.strftime('%H:%M:%S') )

Résultats des opérations

start waiting1: 20:03:30
start waiting2: 20:03:30
start join: 20:03:30
end join: 20:03:30 # Le fil principal est terminé ici , Et les sous - fils continuent à travailler 
Thread1 J'ai reçu l'ordre de couvrir # Sous - thread1 Continuez à travailler 
stop waiting1: 20:03:33
Thread2 J'ai reçu l'ordre de couvrir
stop waiting2: 20:03:38

Conclusions

  1. Le minuteur appartient au fil principal , Tout le thread principal est sur le thread 1Et les fils2Après, Saisissez le module de synchronisation ,Fin du fil principal
  2. Fin du fil principal, Mais n'affecte pas les fils 1Et les fils2Fonctionnement, Donc les fils suivants 1Et les fils2 Toujours à couvrir , Ce n'est qu'à ce moment - là que l'ensemble du processus s'est terminé

Deuxième type:Fils de démon

Pour ouvrir le fil**setDaemon(True),**Définir le thread enfant comme un thread gardien, Mettre en œuvre la fin du programme principal , Le Sous - programme termine immédiatement toutes les fonctions

import threading, time
def doWaiting1():
print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(3)
print("Thread1 J'ai reçu l'ordre de couvrir ")
print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(8)
print("Thread2 J'ai reçu l'ordre de couvrir ")
print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")
tsk = []
# Créer et ouvrir des fils1
thread1 = threading.Thread(target = doWaiting1)
thread1.setDaemon(True)
thread1.start()
tsk.append(thread1)
# Créer et ouvrir des fils2
thread2 = threading.Thread(target = doWaiting2)
thread2.setDaemon(True)
thread2.start()
tsk.append(thread2)
print('start join: ' + time.strftime('%H:%M:%S') )
print('end join: ' + time.strftime('%H:%M:%S') )

Résultats des opérations:

start waiting1: 20:10:04
start waiting2: 20:10:04
start join: 20:10:04
end join: 20:10:04 # Une fois le fil principal terminé, Le Sous - fil se termine immédiatement , Quel que soit l'état .

Conclusions

  1. Une fois le fil principal terminé, Quel que soit le Sous - thread 1,2 Si l'exécution est terminée , C'est fini, plus de descente

La troisième:AdhésionjoinSynchronisation des paramètres de la méthode(Fils non gardiens,EtjoinNe pas définir le délai)

Fils non gardiens, Le programme principal attendra que tous les sous - programmes soient exécutés avant de se terminer

import threading, time
def doWaiting1():
print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(3)
print("Thread1 J'ai reçu l'ordre de couvrir ")
print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(8)
print("Thread2 J'ai reçu l'ordre de couvrir ")
print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")
tsk = []
# Créer et ouvrir des fils1
thread1 = threading.Thread(target = doWaiting1)
thread1.start()
tsk.append(thread1)
# Créer et ouvrir des fils2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)
print('start join: ' + time.strftime('%H:%M:%S') )
for t in tsk:
print('%s Voilà le fil '%t)
t.join() # Threadjoin() C'est - à - dire:: Les deux fils tournent à nouveau , Ne passez pas à la ligne suivante tant que vous n'avez pas terminé 
print('end join: ' + time.strftime('%H:%M:%S') )

Résultats des opérations:

start waiting1: 20:14:35
start waiting2: 20:14:35
start join: 20:14:35
<Thread(Thread-1, started 19648)> Voilà le fil
Thread1 J'ai reçu l'ordre de couvrir
stop waiting1: 20:14:38
<Thread(Thread-2, started 24056)> Voilà le fil
Thread2 J'ai reçu l'ordre de couvrir
stop waiting2: 20:14:43
end join: 20:14:43 # Une fois les deux Threads terminés , Pour que ça marche 

Conclusions:

  1. UtiliserjoinFonctions,Le fil principal sera bloqué(C'est - à - dire::Le fil principal, Spécifie le thread qui crée le Sous - thread ), Attendre d'être utilisé join Exécution du Thread de la méthode terminée
  2. start join Oui.35Secondes,stop waiting1In38Secondes,Juste à temps.sleepC'est3Secondes,stop waiting2- Oui.43Secondes,Juste à temps.sleepC'est8Secondes,Cela montre aussi,Thread1Et2 C'est presque simultané , Mais en raison du temps d'exécution incohérent , Le temps de blocage est donc différent ,Finalend join Il est temps que le dernier thread soit terminé , Toute la procédure a été interrompue à 43Secondes
  3. Oui. Tous les Threads dans une liste , Utilisez tous les Threads de la liste en boucle joinJugement méthodologique, C'est aussi pour s'assurer que tous les sous - threads peuvent fonctionner complètement ,Le fil principal est sorti.

Type 4:AdhésionjoinSynchronisation des paramètres de la méthode(Fils non gardiens,joinDéfinir le délai)

Voilà.joinParamètrestimeoutValeur numérique, Le Sous - thread n'est pas terminé après avoir jugé qu'il y a beaucoup d'attente , Alors le fil principal n'attend plus

Notes:join Après avoir réglé le délai ,Base de jugement Sous - thread terminé | Temps mort (Logique ou relationnelle), C'est - à - dire deux conditions qui sont vraies en premier , Faites - le descendre .

import threading, time
def doWaiting1():
print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(2)
print("Thread1 J'ai reçu l'ordre de couvrir ")
print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(8)
print("Thread2 J'ai reçu l'ordre de couvrir ")
print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")
tsk = []
# Créer et ouvrir des fils1
thread1 = threading.Thread(target = doWaiting1)
thread1.start()
tsk.append(thread1)
# Créer et ouvrir des fils2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)
print('start join: ' + time.strftime('%H:%M:%S') )
for t in tsk:
print("C'est parti.:"+time.strftime('%H:%M:%S'))
print('%s Voilà le fil '%t)
t.join(5)
print("Fin:" + time.strftime('%H:%M:%S'))
print('end join: ' + time.strftime('%H:%M:%S') )

Résultats des opérations:

start waiting1: 21:14:25
start waiting2: 21:14:25
start join: 21:14:25
C'est parti.:21:14:25
<Thread(Thread-1, started 22348)> Voilà le fil
Thread1 J'ai reçu l'ordre de couvrir
stop waiting1: 21:14:27
Fin:21:14:27
C'est parti.:21:14:27
<Thread(Thread-2, started 13164)> Voilà le fil
Fin:21:14:32
end join: 21:14:32
Thread2 J'ai reçu l'ordre de couvrir
stop waiting2: 21:14:33

Conclusions

  1. Voilà.join Après avoir réglé le temps d'attente , Après avoir dépassé le temps d'attente , Fin du fil principal , Mais cela n'affecte pas la poursuite du sous - thread , Tous les sous - threads égaux sont en cours d'exécution et l'ensemble du programme est terminé
  2. Temps d'attente maximum possible pour tous les Threads timeout_total ≤ timeout * Nombre de fils
  3. Bien quetimeoutLe réglage est5s,Mais les fils1C'est tout.2s, Donc le cycle commence et se termine ,Il suffit de consommer2s(21:14:27 - 21:14:25), Ce cycle passe à la deuxième fois , Une seconde attente peut encore être assignée 5s(21:14:32 - 21:14:27), Donc le temps d'attente total pour les deux 2+5=7s,Mais les fils2 Le temps de fonctionnement est 8s,Et8sDe21:14:25Au début,L'heure de fin est21:14:33,Parce quejoin Le temps d'attente est terminé, le programme principal est terminé , Mais n'affecte pas les fils 2Continuez à courir,Donc, dansend joinAprès,Thread2 Les résultats du rapport sont toujours produits ( Parce que le fil du démon n'est pas allumé )
  4. Des articles individuels expliquent joinCe temps est: Le fil principal attendra plusieurs fils timeoutSomme cumulative,Cette déclaration est inexacte,Par3 Le raisonnement de , Pas nécessairement attendre “Nombre de fils* timeout”Tant de temps,Mais...≤“Nombre de fils*timeout”,

Cinquième catégorie:AdhésionjoinSynchronisation des paramètres de la méthode(Fils de démon,joinDéfinir le délai)

Le Sous - thread qui a expiré et n'a pas terminé le traitement sera terminé directement ( Caractéristiques compatibles avec les fils du démon )

import threading, time
def doWaiting1():
print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(2)
print("Thread1 J'ai reçu l'ordre de couvrir ")
print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
time.sleep(8)
print("Thread2 J'ai reçu l'ordre de couvrir ")
print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")
tsk = []
# Créer et ouvrir des fils1
thread1 = threading.Thread(target = doWaiting1)
thread1.setDaemon(True) # Fils de démon
thread1.start()
tsk.append(thread1)
# Créer et ouvrir des fils2
thread2 = threading.Thread(target = doWaiting2)
thread2.setDaemon(True) # Fils de démon
thread2.start()
tsk.append(thread2)
print('start join: ' + time.strftime('%H:%M:%S') )
for t in tsk:
print("C'est parti.:"+time.strftime('%H:%M:%S'))
print('%s Voilà le fil '%t)
t.join(5)
print("Fin:" + time.strftime('%H:%M:%S'))
print('end join: ' + time.strftime('%H:%M:%S') )

Résultats des opérations:

start waiting1: 21:24:14
start waiting2: 21:24:14
start join: 21:24:14
C'est parti.:21:24:14
<Thread(Thread-1, started daemon 9060)> Voilà le fil
Thread1 J'ai reçu l'ordre de couvrir
stop waiting1: 21:24:16
Fin:21:24:16
C'est parti.:21:24:16
<Thread(Thread-2, started daemon 13912)> Voilà le fil
Fin:21:24:21
end join: 21:24:21

Conclusions

  1. Comparé à la quatrième , Le thread principal s'exécute à end join Et c'est fini ,Sous - thread2 A été interrompu pour arrêter de fonctionner

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved