Python 3 - programowanie wielowątkowe
Uruchamianie kilku wątków jest podobne do jednoczesnego uruchamiania kilku różnych programów, ale ma następujące zalety -
Wiele wątków w procesie współdzieli tę samą przestrzeń danych z głównym wątkiem i dzięki temu może łatwiej udostępniać informacje lub komunikować się ze sobą, niż gdyby były oddzielnymi procesami.
Wątki są czasami nazywane procesami lekkimi i nie wymagają dużego narzutu pamięci; są tańsze niż procesy.
Wątek ma początek, sekwencję wykonywania i zakończenie. Posiada wskaźnik instrukcji, który śledzi, gdzie w kontekście jest aktualnie uruchomiona.
Może być uprzedzony (przerwany).
Można go tymczasowo zawiesić (znanego również jako spanie), podczas gdy inne wątki są uruchomione - nazywa się to ustępowaniem.
Istnieją dwa różne rodzaje wątków -
- wątek jądra
- wątek użytkownika
Wątki jądra są częścią systemu operacyjnego, podczas gdy wątki przestrzeni użytkownika nie są zaimplementowane w jądrze.
Istnieją dwa moduły, które obsługują użycie wątków w Pythonie3 -
- _thread
- threading
Moduł wątku był „przestarzały” przez długi czas. Zachęcamy użytkowników do używania zamiast tego modułu obsługi wątków. Stąd w Pythonie 3 moduł „wątek” nie jest już dostępny. Jednak jego nazwa została zmieniona na „_thread” w celu zapewnienia zgodności wstecznej w Pythonie3.
Rozpoczęcie nowego wątku
Aby odrodzić kolejny wątek, musisz wywołać następującą metodę dostępną w module wątków -
_thread.start_new_thread ( function, args[, kwargs] )
To wywołanie metody umożliwia szybkie i wydajne tworzenie nowych wątków w systemie Linux i Windows.
Wywołanie metody wraca natychmiast, a wątek potomny uruchamia się i wywołuje funkcję z przekazaną listą argumentów . Kiedy funkcja zwraca, wątek zostaje zakończony.
Tutaj argumenty to krotka argumentów; użyj pustej krotki, aby wywołać funkcję bez przekazywania żadnych argumentów. kwargs to opcjonalny słownik argumentów słów kluczowych.
Przykład
#!/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
Wynik
Wykonanie powyższego kodu daje następujący wynik -
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
Program przechodzi w nieskończoną pętlę. Będziesz musiał nacisnąć ctrl-c, aby zatrzymać
Chociaż jest bardzo skuteczny w przypadku wątków niskiego poziomu, moduł wątków jest bardzo ograniczony w porównaniu z nowszym modułem wątków.
Moduł gwintowania
Nowszy moduł obsługi wątków dołączony do Pythona 2.4 zapewnia znacznie wydajniejszą obsługę wątków na wysokim poziomie niż moduł wątków omówiony w poprzedniej sekcji.
Gwintowania Moduł udostępnia wszystkie te metody z gwintem modułu i zapewnia dodatkowe metody -
threading.activeCount() - Zwraca liczbę aktywnych obiektów wątku.
threading.currentThread() - Zwraca liczbę obiektów wątku w kontrolce wątku wywołującego.
threading.enumerate() - Zwraca listę wszystkich aktualnie aktywnych obiektów wątku.
Oprócz metod moduł wątkowości ma klasę Thread, która implementuje wątkowanie. Metody udostępniane przez klasę Thread są następujące -
run() - Metoda run () jest punktem wejścia dla wątku.
start() - Metoda start () uruchamia wątek przez wywołanie metody run.
join([time]) - Join () czeka na zakończenie wątków.
isAlive() - Metoda isAlive () sprawdza, czy wątek nadal jest wykonywany.
getName() - Metoda getName () zwraca nazwę wątku.
setName() - Metoda setName () ustawia nazwę wątku.
Tworzenie wątku za pomocą modułu Threading
Aby zaimplementować nowy wątek za pomocą modułu Threading, musisz wykonać następujące czynności -
Zdefiniuj nową podklasę klasy Thread .
Zastąp metodę __init __ (self [, args]) , aby dodać dodatkowe argumenty.
Następnie przesłoń metodę run (self [, args]), aby zaimplementować, co wątek powinien zrobić po uruchomieniu.
Po utworzeniu nowej podklasy Thread można utworzyć jej instancję, a następnie rozpocząć nowy wątek, wywołując metodę start () , która z kolei wywołuje metodę run () .
Przykład
#!/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")
Wynik
Kiedy uruchamiamy powyższy program, daje on następujący wynik -
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
Synchronizacja wątków
Moduł obsługi wątków dostarczany z Pythonem zawiera prosty w implementacji mechanizm blokujący, który umożliwia synchronizację wątków. Nowa blokada jest tworzona przez wywołanie metody Lock () , która zwraca nową blokadę.
Metoda pozyskiwania (blokowania) nowego obiektu blokady służy do wymuszenia synchronicznego działania wątków. Opcjonalny parametr blokowania umożliwia kontrolowanie, czy wątek oczekuje na uzyskanie blokady.
Jeśli blokowanie jest ustawione na 0, wątek zwraca natychmiast wartość 0, jeśli nie można uzyskać blokady, oraz 1, jeśli blokada została uzyskana. Jeśli blokowanie jest ustawione na 1, wątek blokuje się i czeka na zwolnienie blokady.
Metoda release () nowego obiektu blokady służy do zwolnienia blokady, gdy nie jest już potrzebna.
Przykład
#!/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")
Wynik
Wykonanie powyższego kodu daje następujący wynik -
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
Wielowątkowa kolejka priorytetowa
Queue Moduł pozwala na tworzenie nowego obiektu kolejki, która może pomieścić szereg specyficzny elementów. Istnieją następujące metody kontrolowania kolejki -
get() - Funkcja get () usuwa i zwraca element z kolejki.
put() - Put dodaje element do kolejki.
qsize() - Funkcja qsize () zwraca liczbę elementów, które aktualnie znajdują się w kolejce.
empty()- Empty () zwraca True, jeśli kolejka jest pusta; w przeciwnym razie False.
full()- full () zwraca True, jeśli kolejka jest pełna; w przeciwnym razie False.
Przykład
#!/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")
Wynik
Wykonanie powyższego kodu daje następujący wynik -
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