Python - 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 czasami nazywane są procesami lekkimi i nie wymagają dużej ilości 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.

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/python

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

Wykonanie powyższego kodu daje następujący wynik -

Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009

Chociaż jest bardzo skuteczny w przypadku wątków niskiego poziomu, ale moduł wątków jest bardzo ograniczony w porównaniu z nowszym modułem wątków.

Threading Moduł

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żesz 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/python

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, 5, self.counter)
      print "Exiting " + self.name

def print_time(threadName, counter, delay):
   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()

print "Exiting Main Thread"

Wykonanie powyższego kodu daje następujący wynik -

Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2

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 wymuszania 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/python

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"

Wykonanie powyższego kodu daje następujący wynik -

Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
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/python

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"

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