Python 3 - Multithread-Programmierung

Das Ausführen mehrerer Threads ähnelt dem gleichzeitigen Ausführen mehrerer verschiedener Programme, bietet jedoch die folgenden Vorteile:

  • Mehrere Threads innerhalb eines Prozesses teilen sich den gleichen Datenraum mit dem Hauptthread und können daher einfacher Informationen austauschen oder miteinander kommunizieren, als wenn es sich um separate Prozesse handeln würde.

  • Threads werden manchmal als leichte Prozesse bezeichnet und erfordern nicht viel Speicheraufwand. Sie sind billiger als Prozesse.

Ein Thread hat einen Anfang, eine Ausführungssequenz und eine Schlussfolgerung. Es verfügt über einen Anweisungszeiger, der verfolgt, wo in seinem Kontext es gerade ausgeführt wird.

  • Es kann vorbelegt (unterbrochen) werden.

  • Es kann vorübergehend angehalten werden (auch als Ruhezustand bezeichnet), während andere Threads ausgeführt werden - dies wird als Nachgeben bezeichnet.

Es gibt zwei verschiedene Arten von Threads -

  • Kernel-Thread
  • Benutzer-Thread

Kernel-Threads sind Teil des Betriebssystems, während die User-Space-Threads nicht im Kernel implementiert sind.

Es gibt zwei Module, die die Verwendung von Threads in Python3 unterstützen -

  • _thread
  • threading

Das Thread-Modul ist seit geraumer Zeit "veraltet". Benutzer werden aufgefordert, stattdessen das Threading-Modul zu verwenden. Daher ist in Python 3 das Modul "Thread" nicht mehr verfügbar. Aus Gründen der Abwärtskompatibilität in Python3 wurde es jedoch in "_thread" umbenannt.

Einen neuen Thread starten

Um einen anderen Thread zu erzeugen, müssen Sie die folgende im Thread- Modul verfügbare Methode aufrufen :

_thread.start_new_thread ( function, args[, kwargs] )

Dieser Methodenaufruf ermöglicht eine schnelle und effiziente Möglichkeit, neue Threads unter Linux und Windows zu erstellen.

Der Methodenaufruf wird sofort zurückgegeben und der untergeordnete Thread startet und ruft die Funktion mit der übergebenen Liste von Argumenten auf . Wenn die Funktion zurückkehrt, wird der Thread beendet.

Hier ist args ein Tupel von Argumenten; Verwenden Sie ein leeres Tupel, um die Funktion aufzurufen, ohne Argumente zu übergeben. kwargs ist ein optionales Wörterbuch mit Schlüsselwortargumenten.

Beispiel

#!/usr/bin/python3

import _thread
import time

# Define a function for the thread
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# Create two threads as follows
try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: unable to start thread")

while 1:
   pass

Ausgabe

Wenn der obige Code ausgeführt wird, wird das folgende Ergebnis erzeugt:

Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016

Das Programm läuft in einer Endlosschleife. Sie müssen Strg-C drücken, um zu stoppen

Obwohl es für Threading auf niedriger Ebene sehr effektiv ist, ist das Thread- Modul im Vergleich zum neueren Threading-Modul sehr begrenzt.

Das Threading-Modul

Das neuere Threading-Modul in Python 2.4 bietet eine viel leistungsfähigere Unterstützung auf hoher Ebene für Threads als das im vorherigen Abschnitt beschriebene Thread-Modul.

Das Einfädeln Modul aussetzt alle Methoden des Fadenmoduls und bietet einige zusätzliche Methoden -

  • threading.activeCount() - Gibt die Anzahl der aktiven Thread-Objekte zurück.

  • threading.currentThread() - Gibt die Anzahl der Thread-Objekte im Thread-Steuerelement des Aufrufers zurück.

  • threading.enumerate() - Gibt eine Liste aller aktuell aktiven Thread-Objekte zurück.

Zusätzlich zu den Methoden verfügt das Threading-Modul über die Thread- Klasse, die das Threading implementiert. Die von der Thread- Klasse bereitgestellten Methoden lauten wie folgt:

  • run() - Die run () -Methode ist der Einstiegspunkt für einen Thread.

  • start() - Die Methode start () startet einen Thread durch Aufrufen der Methode run.

  • join([time]) - Join () wartet auf das Beenden von Threads.

  • isAlive() - Die Methode isAlive () prüft, ob noch ein Thread ausgeführt wird.

  • getName() - Die Methode getName () gibt den Namen eines Threads zurück.

  • setName() - Die Methode setName () legt den Namen eines Threads fest.

Thread mit Threading-Modul erstellen

Um einen neuen Thread mit dem Threading-Modul zu implementieren, müssen Sie Folgendes tun:

  • Definieren Sie eine neue Unterklasse der Thread- Klasse.

  • Überschreiben der __init __ (self [, args]) Verfahren zusätzliche Argumente hinzuzufügen.

  • Überschreiben Sie dann die Methode run (self [, args]), um zu implementieren, was der Thread beim Starten tun soll.

Nachdem Sie die neue Thread- Unterklasse erstellt haben, können Sie eine Instanz davon erstellen und dann einen neuen Thread starten , indem Sie start () aufrufen , das wiederum die run () -Methode aufruft .

Beispiel

#!/usr/bin/python3

import threading
import time

exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting " + self.name)
      print_time(self.name, self.counter, 5)
      print ("Exiting " + self.name)

def print_time(threadName, delay, counter):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")

Ergebnis

Wenn wir das obige Programm ausführen, wird das folgende Ergebnis erzeugt:

Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread

Threads synchronisieren

Das mit Python gelieferte Threading-Modul enthält einen einfach zu implementierenden Sperrmechanismus, mit dem Sie Threads synchronisieren können. Eine neue Sperre wird durch Aufrufen der Lock () -Methode erstellt, die die neue Sperre zurückgibt.

Die Erfassungsmethode (Blockierungsmethode) des neuen Sperrobjekts wird verwendet, um zu erzwingen, dass die Threads synchron ausgeführt werden. Mit dem optionalen Blockierungsparameter können Sie steuern, ob der Thread darauf wartet, die Sperre zu erhalten.

Wenn das Blockieren auf 0 gesetzt ist, kehrt der Thread sofort mit einem 0-Wert zurück, wenn die Sperre nicht erfasst werden kann, und mit einer 1, wenn die Sperre erworben wurde. Wenn die Blockierung auf 1 gesetzt ist, blockiert der Thread und wartet, bis die Sperre aufgehoben wird.

Die release () -Methode des neuen Sperrobjekts wird verwendet, um die Sperre aufzuheben, wenn sie nicht mehr benötigt wird.

Beispiel

#!/usr/bin/python3

import threading
import time

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting " + self.name)
      # Get lock to synchronize threads
      threadLock.acquire()
      print_time(self.name, self.counter, 3)
      # Free lock to release next thread
      threadLock.release()

def print_time(threadName, delay, counter):
   while counter:
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

threadLock = threading.Lock()
threads = []

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()

# Add threads to thread list
threads.append(thread1)
threads.append(thread2)

# Wait for all threads to complete
for t in threads:
   t.join()
print ("Exiting Main Thread")

Ausgabe

Wenn der obige Code ausgeführt wird, wird das folgende Ergebnis erzeugt:

Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
Exiting Main Thread

Multithread-Prioritätswarteschlange

Mit dem Warteschlangenmodul können Sie ein neues Warteschlangenobjekt erstellen, das eine bestimmte Anzahl von Elementen enthalten kann. Es gibt folgende Methoden zur Steuerung der Warteschlange:

  • get() - Mit get () wird ein Element aus der Warteschlange entfernt und zurückgegeben.

  • put() - Der Put fügt einer Warteschlange ein Element hinzu.

  • qsize() - qsize () gibt die Anzahl der Elemente zurück, die sich derzeit in der Warteschlange befinden.

  • empty()- Das empty () gibt True zurück, wenn die Warteschlange leer ist. sonst falsch.

  • full()- full () gibt True zurück, wenn die Warteschlange voll ist. sonst falsch.

Beispiel

#!/usr/bin/python3

import queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, q):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.q = q
   def run(self):
      print ("Starting " + self.name)
      process_data(self.name, self.q)
      print ("Exiting " + self.name)

def process_data(threadName, q):
   while not exitFlag:
      queueLock.acquire()
      if not workQueue.empty():
         data = q.get()
         queueLock.release()
         print ("%s processing %s" % (threadName, data))
      else:
         queueLock.release()
         time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = queue.Queue(10)
threads = []
threadID = 1

# Create new threads
for tName in threadList:
   thread = myThread(threadID, tName, workQueue)
   thread.start()
   threads.append(thread)
   threadID += 1

# Fill the queue
queueLock.acquire()
for word in nameList:
   workQueue.put(word)
queueLock.release()

# Wait for queue to empty
while not workQueue.empty():
   pass

# Notify threads it's time to exit
exitFlag = 1

# Wait for all threads to complete
for t in threads:
   t.join()
print ("Exiting Main Thread")

Ausgabe

Wenn der obige Code ausgeführt wird, wird das folgende Ergebnis erzeugt:

Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread