Heim  >  Artikel  >  Backend-Entwicklung  >  Python-Entwicklung – detaillierte Erklärung von Prozessen, Threads und Coroutinen

Python-Entwicklung – detaillierte Erklärung von Prozessen, Threads und Coroutinen

零下一度
零下一度Original
2017-06-27 09:57:521518Durchsuche

Was ist ein Prozess?

Ein Programm kann nicht alleine ausgeführt werden. Nur wenn das Programm in den Speicher geladen wird und das System ihm Ressourcen zuweist, wird es als Prozess bezeichnet. Der Unterschied zwischen einem Programm und einem Prozess besteht darin, dass ein Programm eine Sammlung von Anweisungen ist, bei denen es sich um einen statischen Beschreibungstext des Prozesses handelt. Ein Prozess ist eine Ausführungsaktivität des Programms und ein dynamisches Konzept.

Was ist ein Thread?

Thread ist die kleinste Einheit, mit der das Betriebssystem die Betriebsplanung durchführen kann. Es wird in den Prozess eingebunden und ist die eigentliche Bedieneinheit im Prozess. Ein Thread bezieht sich auf einen einzelnen sequenziellen Kontrollfluss in einem Prozess. In einem Prozess können mehrere Threads gleichzeitig ausgeführt werden, und jeder Thread führt parallel unterschiedliche Aufgaben aus.

Was ist der Unterschied zwischen Prozess und Thread?

Threads teilen sich den Speicherplatz und der Speicher des Prozesses ist unabhängig.

Threads desselben Prozesses können direkt miteinander kommunizieren, zwei Prozesse müssen jedoch über einen Zwischenagenten miteinander kommunizieren.

Das Erstellen eines neuen Threads ist sehr einfach. Das Erstellen eines neuen Prozesses erfordert das Klonen seines übergeordneten Prozesses.

Ein Thread kann andere Threads im selben Prozess steuern und betreiben, aber ein Prozess kann nur untergeordnete Prozesse betreiben.

Python GIL (Global Interpreter Lock)

Egal wie viele Threads geöffnet sind und wie viele CPUs vorhanden sind, Python lässt bei der Ausführung nur einen Thread gleichzeitig zu.

Python-Threading-Modul

ruft direkt

  1. Import-Threading, Zeit
  2. def run_num(num):
  3. ""
  4. Definieren Sie die Funktion, die vom Thread ausgeführt werden soll
  5. :param num:
  6. :zurück:
  7. """
  8. print("running on number:%s"%num)
  9. time.sleep(3 )
  10. if __name__ == '__main__':
  11. # Erzeuge eine Thread-Instanz t1
  12. t1 = threading.Thread(target=run_num,args=( 1,) )
  13. # Erzeuge eine Thread-Instanz t2
  14. t2 = Threading. Thread(target =run_num,args=(2,))
  15.  ​ # Thread t1 starten
  16. t1.start()
  17. # Thread t2 starten
  18. t2 .start()
  19. # Thread-Namen abrufen
  20. print(t1.getName ())
  21. print(t2.getName())
  22. Ausgabe:
  23. Laufen auf Nummer:1
  24. Laufen auf Nummer:2
  25. Thread-1
  26. Thread-2
Geerbter Aufruf

  1. Threading importieren, Zeit
  2. class MyThread(threading.Thread):
  3. def __init__(self,num ):
  4. threading.Thread.__init__(self)
  5. self.num = num
  6. ​ # Definieren Sie die Funktion, die von jedem Thread ausgeführt werden soll. Der Funktionsname muss ausgeführt werden
  7. def run(self):
  8. print("running on number:%s"%self.num)
  9. time.sleep(3)
  10. if __name__ == '__main__':
  11. t1 = MyThread(1)
  12. t2 = MyThread(2)
  13. t1.start()
  14. t2.start()
  15. Ausgabe:
  16. läuft auf Nummer:1
  17. läuft auf Nummer:2
  18. Join and Daemon

    Join

    Die Funktion von Join besteht darin, den Hauptprozess zu blockieren und das Programm nach dem Join nicht mehr ausführen zu können.

    Bei Multithreads und mehreren Joins werden die Join-Methoden jedes Threads nacheinander ausgeführt. Die Ausführung des vorherigen Threads ist abgeschlossen, bevor der nachfolgende Thread ausgeführt werden kann.

    Wenn keine Parameter vorhanden sind, warten Sie, bis der Thread beendet ist, bevor Sie nachfolgende Programme ausführen.

    Warten Sie nach dem Festlegen der Parameter auf die vom Thread festgelegte Zeit und führen Sie dann den nachfolgenden Hauptprozess aus, unabhängig davon, ob der Thread endet.

    1. Einfädeln, Zeit importieren
    2. Klasse MyThread(threading.Thread):
    3. def __init__(self,num):
    4.                                                                        threading.__init__(self)<.>
      # Definieren Sie die Funktion, die von jedem Thread ausgeführt werden soll. Der Funktionsname muss ausgeführt werden
    5. def run(self):
    6. print("
    7. running on number:%s
    8. "%self. num)
    9. time.sleep(3)
      print("
    10. Thread:%s
    11. "%self.num)
    12. if
      __name__ == '__main__':
    13. t1 = MyThread(1)
      t2 = MyThread(2)
    14. t1.start()
    15. t1.join( )
    16.     t2.start()
    17.     t2.join()
    18. 输出:
    19. läuft auf Nummer:1
    20. Thread:1
    21. läuft auf Nummer:2
    22. Thread:2

    设置参数效果如下:

    1. if __name__ == '__main__':
    2.     t1 = MyThread(1)
    3.     t2 = MyThread(2)
    4.     t1.start()
    5.     t1.join(2)
    6.     t2.start()
    7.     t2.join()
    8. 输出:
    9. Laufen auf Nummer:1
    10. Laufen auf Nummer:2
    11. Thread:1
    12. Thread:2

    Daemon

    默认情况下,主线程在退出时会等待所有子线程的结束。如果希望主线程不等待子线程,而是在退出时自动结束所有的子线程,就需要设置子线程为后台线程(daemon)。方法是通过调用线程类的setDaemon()方法。

    1. Importzeit, Einfädeln
    2. def run(n):
    3.     print("%s".center(20,"*")%n)
    4.     time.sleep(2)
    5.     print("done".center(20,"*" ))
    6. def main():
    7.     for i in range(5):
    8.         t = threading.Thread(target =run,args=(i,))
    9.         t.start()
    10.         t.join(1)
    11.         print("Startthread",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("Hauptthread fertig". center(20,"*"))
    18. Ausgabe:
    19. ***************0************
    20. Startthread Thread- 2
    21. **********1**********
    22. ********fertig***************
    23. Startthread Thread -3
    24. *********2**********
    25. **Hauptthread fertig **

    Thread-Sperre (Mutex)

    Mehrere Threads können unter einem Prozess gestartet werden, und mehrere Threads teilen sich den Speicherplatz des übergeordneten Prozesses. Dies bedeutet, dass jeder Thread auf dieselben Daten zugreifen kann. Wenn zwei Threads gleichzeitig dieselben Daten ändern möchten, ist eine Thread-Sperre erforderlich.

    1. Importzeit, Einfädeln
    2. def addNum():
    3. ​ # Diese globale Variable in jedem Thread abrufen
    4. global num
    5. print("--get num:",num)
    6. time.sleep(1)
    7. # -1-Operation für diese öffentliche Variable ausführen
    8. num -= 1
    9. # Eine gemeinsame Variable festlegen
    10. num = 100
    11. thread_list = []
    12. für i im Bereich(100):
    13. t = threading.Thread(target=addNum)
    14. t.start()
    15. thread_list.append(t)
    16. # Warten, bis die Ausführung aller Threads abgeschlossen ist
    17. for t in thread_list:
    18. t.join()
    19. print("final num:",num)

    Gesperrte Version

    Lock verhindert, dass andere Threads den Ressourcenzugriff teilen , und derselbe Thread kann nur einmal akquirieren, wenn mehr als einmal ein Deadlock auftritt und das Programm nicht weiter ausgeführt werden kann.

    1. Importzeit, Einfädeln
    2. def addNum():
    3. ​ # Diese globale Variable in jedem Thread abrufen
    4. global num
    5. print("--get num:",num)
    6. time.sleep(1)
    7. # Vor dem Ändern von Daten sperren
    8. lock.acquire()
    9. # -1-Operation für diese öffentliche Variable ausführen
    10. num -= 1
    11. # Freigabe nach Änderung
    12. lock.release()
    13. # Eine gemeinsam genutzte Variable festlegen
    14. num = 100
    15. thread_list = []
    16. # Globale Sperre generieren
    17. lock = threading.Lock()
    18. for i im Bereich ( 100):
    19. t = threading.Thread(target=addNum)
    20. t .start()
    21. thread_list.append(t)
    22. # Warte auf alle Thread-Ausführung abgeschlossen
    23. für t in thread_list:
    24. t.join()
    25. print("final num: ",num)

    GIL VS Lock

    GIL garantiert, dass nur ein Thread gleichzeitig ausgeführt werden kann. Lock ist eine Sperre auf Benutzerebene und hat nichts mit GIL zu tun.

    Rlock (rekursive Sperre)

    Rlock darf mehrmals im selben Thread erworben werden. Die Freigabe gemeinsam genutzter Ressourcen durch einen Thread erfordert die Freigabe aller Schlösser. Das heißt, n-maliges Erfassen erfordert n-maliges Freigeben.

    1. def run1():
    2. print("Erste Teildaten abrufen")
    3. lock.acquire()
    4. globale Zahl
    5. Zahl += 1
    6. Sperre .release()
    7. return num
    8. def run2():
    9. print("grab the second part data")
    10. lock.acquire()
    11. global num2
    12. num2 += 1
    13. lock.release()
    14.  return num2
    15. def run3():
    16.      lock.acquire()
    17.     res = run1()
    18.     print ("zwischen Lauf1 und Lauf2".center(50,"*"))
    19.     res2 = run2 ()
    20.     lock.release()
    21.     print (res,res2)
    22. if __name__ == '__main__':
    23.     num,num2 = 0,0
    24.     lock = threading.RLock()
    25.     for i in range(10):
    26.         t = threading.Thread(target=run3)
    27.         t.start()
    28. while threading.active_count() != 1:
    29.     print(threading.active_count())
    30. else:
    31.     print("alle Threads erledigt".center(50,"*"))
    32.     print(num,num2)

    这两种锁的主要区别是,RLock允许在同一线程中被多次acquire。 „Sperren却不允许这种情况.注意, 如果使用RLock, 那么acquire和release必须成对出现, 即调用了n次acquire, 必须调用n次的release才能真正释放所占用的锁.

    Semaphore (信号量)

    互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据, 比如售票处有3个窗口, 那最多只允许3个人同时买票" >

    1. def run(n):
    2.     semaphore.acquire()
    3.     time.sleep(1)
    4.     print("
    5. Führen Sie den Thread aus:%s
    6. "%n)
    7.     semaphore.release()
    8. if
      __name__ == '__main__':
    9.     # 最多允许5个线程同时运行
          semaphore = threading.BoundedSemaphore(5)
    10.     
    11. für
    12. i
      im Bereich(20):
    13. t = threading.Thread(target=run,args=(i,))
    14. t.start ()
    15. while threading.active_count() != 1:
    16. # print(threading.active_count())
    17. pass
    18. else:
    19. print("alle Threads erledigt ".center(50,"*"))

    Timer (Timer)

    Timer ruft eine Funktion in einem bestimmten Intervall auf Wenn Sie ab und zu eine Funktion aufrufen möchten, müssen Sie den Timer in der vom Timer aufgerufenen Funktion erneut festlegen. Timer ist eine abgeleitete Klasse von Thread.

    1. Threading importieren
    2. def hallo( ):
    3. print("Hallo Welt!")
    4. # Hallo-Funktion nach 5 Sekunden Verzögerung ausführen
    5. t = threading.Timer(5,hello)
    6. t.start()

    Event

    Python stellt das Event-Objekt für die Kommunikation zwischen Threads bereit, das über Thread-Einstellungen verfügt Das Signalflag ist falsch. Der Thread wartet darauf, dass das Befehlssignal von anderen Threads auf wahr gesetzt wird. Das Event-Objekt implementiert einen einfachen Thread-Kommunikationsmechanismus. Es stellt Einstellungssignale, Löschsignale, Wartesignale usw. bereit, um die Kommunikation zwischen Threads zu realisieren.

    1. Signal setzen

    Verwenden Sie die set()-Methode von Event, um das Signalflag im Event-Objekt auf true zu setzen. Das Event-Objekt stellt die Methode isSet() bereit, um den Status seines internen Signalflags zu bestimmen. Wenn die Methode set() des Event-Objekts verwendet wird, gibt die Methode isSet() true zurück.

    1. Signal löschen

    Verwenden Sie die Methode „clear()“ von Event, um das Signalflag im Event-Objekt zu löschen, d. h. auf zu setzen Bei Verwendung der Methode „clear()“ von After Event gibt die Methode „isSet()“ „false“ zurück.

    1. Warten

    Die Methode wait() des Ereignisses wird nur dann schnell ausgeführt und schließt die Rückgabe ab, wenn das interne Signal wahr ist. Wenn das interne Signalflag des Event-Objekts „false“ ist, wartet die Methode „wait()“ darauf, dass es „true“ ist, bevor sie zurückkehrt.

    Verwenden Sie Event, um die Interaktion zwischen zwei oder mehr Threads zu realisieren. Nehmen wir die Ampel als Beispiel. Das heißt, wir starten einen Thread, der als Ampel fungiert, und generieren mehrere Threads, die als Vehikel fungieren Fahrzeuge werden nach der Rot-Grün-Regel angehalten.

    1. Threading importieren, Zeit, zufällig
    2. def light():
    3. if not event.isSet():
    4.  Ereignis.set()
    5. count = 0
    6.  while True:
    7.  if count < 5:
    8. print("
    9.         elif count < 8:
    10.             print(" 33[43;1m--gelbes Licht an-- 33[0m".center(50, "*"))
    11.         elif count < 13:
    12.             ifevent.isSet():
    13.                 event.clear()
    14.             print(" 33[41;1m--red Licht an-- 33[0m".center(50,"*"))
    15.         else:
    16.             count = 0
    17.             Ereignis. set()
    18.         time.sleep(1)
    19.         count += 1
    20. def car( n):
    21.     während 1:
    22.         Zeit. sleep(random.randrange(10))
    23.         ifevent.isSet():
    24.             print("Auto %s fährt..."%n)
    25.         else:
    26.             print("Auto %s wartet auf die rote Ampel... "%n)
    27. if __name__ == "__main__":
    28.     event = threading.Event()
    29.     Light = threading.Thread(target=light,)
    30.     Light.start()
    31.     füri imBereich(3):
    32.         t = threading.Thread(target=car,args=(i,))
    33.         t.start()

    Warteschlangenwarteschlange

    Warteschlangen sind in Python die am häufigsten verwendete Form des Datenaustauschs zwischen Threads. Das Warteschlangenmodul ist ein Modul, das Warteschlangenoperationen bereitstellt.

    Warteschlangenobjekt erstellen

    1. Warteschlange importieren
    2. q = queue.Queue(maxsize = 10)

    Die Klasse queue.Queue ist eine synchrone Implementierung einer Warteschlange. Die Warteschlangenlänge kann unbegrenzt oder begrenzt sein. Die Warteschlangenlänge kann über den optionalen Parameter maxsize des Warteschlangenkonstruktors festgelegt werden. Wenn maxsize kleiner als 1 ist, ist die Warteschlangenlänge unbegrenzt.

    Einen Wert in die Warteschlange stellen

    1. q.put("a")

    Rufen Sie die put()-Methode des Warteschlangenobjekts auf, um ein Element am Ende der Warteschlange einzufügen. put() verfügt über zwei Parameter. Das erste Element ist der Wert des eingefügten Elements. Der zweite Block ist ein optionaler Parameter und hat den Standardwert 1. Wenn die Warteschlange derzeit leer ist und der Block 1 ist, bewirkt die Methode put(), dass der aufrufende Thread anhält, bis eine Dateneinheit freigegeben wird. Wenn block 0 ist, löst die put()-Methode eine Full-Ausnahme aus.

    Einen Wert aus der Warteschlange entfernen

    1. q.get()

    Durch Aufrufen der get()-Methode des Warteschlangenobjekts wird ein Element vom Kopf der Warteschlange entfernt und zurückgegeben. Der optionale Parameter ist block, der standardmäßig True ist. Wenn die Warteschlange leer ist und der Block „True“ ist, bewirkt get(), dass der aufrufende Thread anhält, bis ein Element verfügbar wird. Wenn die Warteschlange leer ist und der Block den Wert False hat, löst die Warteschlange eine Empty-Ausnahme aus.

    Das Python-Warteschlangenmodul verfügt über drei Warteschlangen und Konstruktoren

    1. # First in, first out
    2. Klasse queue.Queue(maxsize=0)
    3. # Zuerst rein, zuletzt raus
    4. class queue.LifoQueue(maxsize=0)
    5. # Je niedriger die Prioritätswarteschlange Ebene, die erste, die herauskommt
      Gemeinsame Methoden
    6. q = queue.Queue()

    # Return die Größe der Warteschlange

    1. q.qsize()
    2. # Wenn die Warteschlange ist leer, gibt True zurück, andernfalls False
    3. q.empty()
    4. # Wenn die Warteschlange voll ist, geben Sie True zurück, andernfalls False
    5. q.full()
    6. # Warteschlange abrufen, Timeout-Wartezeit
    7. q.
      get
    8. ([block[,timeout]])
    9. # Entspricht q.
    10. get
      (False)
    11. q.get_nowait ()
    12. # Warten Sie, bis die Warteschlange leer ist, bevor Sie andere Vorgänge ausführen
    13. q .join()

    Produzenten-Konsumenten-Modell

    Die Verwendung der Produzenten- und Konsumentenmuster in der Entwicklung und Programmierung kann die meisten Parallelitätsprobleme lösen. Dieser Modus verbessert die Gesamtdatenverarbeitungsgeschwindigkeit des Programms, indem er die Arbeitsfunktionen des Produktionsthreads und des Verbraucherthreads ausgleicht.

    Warum die Producer- und Consumer-Muster verwenden?

    In der Thread-Welt ist der Producer der Thread, der Daten produziert, und der Consumer ist der Thread, der Daten verbraucht. Wenn bei der Multithread-Entwicklung die Verarbeitungsgeschwindigkeit des Produzenten sehr hoch und die Verarbeitungsgeschwindigkeit des Konsumenten sehr langsam ist, muss der Produzent warten, bis der Konsument die Verarbeitung abgeschlossen hat, bevor er mit der Datenproduktion fortfahren kann. Wenn die Verarbeitungsleistung des Verbrauchers größer ist als die des Produzenten, muss der Verbraucher auf die gleiche Weise auf den Produzenten warten. Um dieses Problem zu lösen, wurden die Produzenten- und Konsumentenmodelle eingeführt.

    Was ist das Produzenten-Konsumenten-Muster?

    Das Produzenten-Konsumenten-Muster verwendet einen Container, um das Problem der starken Kopplung zwischen Produzenten und Konsumenten zu lösen. Produzenten und Konsumenten kommunizieren nicht direkt miteinander, sondern über Blockierungswarteschlangen. Daher wartet der Produzent nicht mehr darauf, dass der Konsument sie verarbeitet, sondern wirft sie direkt in die Blockierungswarteschlange Bitten Sie den Produzenten um Daten, aber nehmen Sie sie direkt aus der Blockierungswarteschlange. Die Blockierungswarteschlange entspricht einem Puffer und gleicht die Verarbeitungskapazitäten von Produzenten und Verbrauchern aus.

    Das grundlegendste Beispiel des Produzenten-Konsumenten-Modells.

    1. Importwarteschlange, Threading, Zeit
    2. q = queue.Queue(maxsize=10)
    3. def Producer():
    4. count = 1
    5. while True:
    6. q.put("Bone %s"%count)
    7. print( „Knochen produziert“,Anzahl)
    8. Anzahl += 1
    9. def Consumer(name):
    10.  while q.qsize ( ) > 0:
    11. print("[%s] hat [%s] bekommen und es gegessen... "%( name,q.get()))
    12. time.sleep(1)
    13. p = threading.Thread(target=Produzent,)
    14. c1 = threading.Thread(target=Consumer,args=("Wangcai",))
    15. c2 = threading.Thread (target=Consumer ,args=("来福",))
    16. p.start()
    17. c1.start()
    18. c2.start()

Das obige ist der detaillierte Inhalt vonPython-Entwicklung – detaillierte Erklärung von Prozessen, Threads und Coroutinen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Zusammenfassung des grundlegenden Django-TutorialsNächster Artikel:Zusammenfassung des grundlegenden Django-Tutorials

In Verbindung stehende Artikel

Mehr sehen