Python 3 - Многопоточное программирование
Запуск нескольких потоков аналогичен одновременному запуску нескольких разных программ, но со следующими преимуществами:
Несколько потоков в процессе используют одно и то же пространство данных с основным потоком и поэтому могут обмениваться информацией или общаться друг с другом более легко, чем если бы они были отдельными процессами.
Потоки иногда называют легковесными процессами, и они не требуют больших затрат памяти; они дешевле процессов.
У потока есть начало, последовательность выполнения и завершение. У него есть указатель инструкции, который отслеживает, где в его контексте он сейчас выполняется.
Его можно упредить (прервать).
Его можно временно приостановить (также известное как спящий), пока работают другие потоки - это называется уступкой.
Есть два разных вида нитей -
- поток ядра
- поток пользователя
Потоки ядра являются частью операционной системы, в то время как потоки пользовательского пространства не реализованы в ядре.
Есть два модуля, которые поддерживают использование потоков в Python3:
- _thread
- threading
Модуль потоков уже давно "устарел". Вместо этого пользователям рекомендуется использовать модуль потоковой передачи. Следовательно, в Python 3 модуль thread больше не доступен. Однако он был переименован в «_thread» для обеспечения обратной совместимости с Python3.
Создание новой темы
Чтобы создать другой поток, вам нужно вызвать следующий метод, доступный в модуле потока:
_thread.start_new_thread ( function, args[, kwargs] )
Вызов этого метода позволяет быстро и эффективно создавать новые потоки как в Linux, так и в Windows.
Вызов метода немедленно возвращается, и дочерний поток запускается и вызывает функцию с переданным списком аргументов . Когда функция возвращается, поток завершается.
Здесь args - это набор аргументов; используйте пустой кортеж для вызова функции без передачи аргументов. kwargs - необязательный словарь аргументов ключевых слов.
пример
#!/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
Вывод
Когда приведенный выше код выполняется, он дает следующий результат:
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
Программа идет в бесконечном цикле. Вам нужно будет нажать ctrl-c, чтобы остановить
Хотя он очень эффективен для низкоуровневых потоков, модуль потоков очень ограничен по сравнению с более новым модулем потоков.
Модуль Threading
Новый модуль потоковой передачи, включенный в Python 2.4, обеспечивает гораздо более мощную и высокоуровневую поддержку потоков, чем модуль потоков, описанный в предыдущем разделе.
Модуль потоковой передачи предоставляет все методы модуля потоков и некоторые дополнительные методы:
threading.activeCount() - Возвращает количество активных объектов потока.
threading.currentThread() - Возвращает количество объектов потока в элементе управления потоком вызывающего объекта.
threading.enumerate() - Возвращает список всех активных в данный момент объектов потока.
В дополнение к методам, в модуле threading есть класс Thread, который реализует потоки. Методы, предоставляемые классом Thread , следующие:
run() - Метод run () - это точка входа для потока.
start() - Метод start () запускает поток, вызывая метод run.
join([time]) - join () ожидает завершения потоков.
isAlive() - Метод isAlive () проверяет, выполняется ли все еще поток.
getName() - Метод getName () возвращает имя потока.
setName() - Метод setName () устанавливает имя потока.
Создание потока с использованием модуля Threading
Чтобы реализовать новый поток с использованием модуля потоковой передачи, вам необходимо сделать следующее:
Определите новый подкласс класса Thread .
Переопределите метод __init __ (self [, args]), чтобы добавить дополнительные аргументы.
Затем переопределите метод run (self [, args]), чтобы реализовать то, что поток должен делать при запуске.
Создав новый подкласс Thread , вы можете создать его экземпляр, а затем запустить новый поток, вызвав метод start () , который, в свою очередь, вызывает метод run () .
пример
#!/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")
Результат
Когда мы запускаем указанную выше программу, она дает следующий результат:
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
Синхронизация потоков
Модуль потоковой передачи, поставляемый с Python, включает простой в реализации механизм блокировки, который позволяет синхронизировать потоки. Новая блокировка создается путем вызова метода Lock () , который возвращает новую блокировку.
Метод получения (блокировки) нового объекта блокировки используется для обеспечения синхронного запуска потоков. Необязательный параметр блокировки позволяет вам контролировать, ожидает ли поток получения блокировки.
Если для блокировки установлено значение 0, поток немедленно возвращается со значением 0, если блокировка не может быть получена, и с 1, если блокировка была получена. Если для блокировки установлено значение 1, поток блокируется и ожидает снятия блокировки.
Метод release () нового объекта блокировки используется для снятия блокировки, когда она больше не требуется.
пример
#!/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")
Вывод
Когда приведенный выше код выполняется, он дает следующий результат:
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
Многопоточная приоритетная очередь
Модуль Queue позволяет вам создать новый объект очереди, который может содержать определенное количество элементов. Существуют следующие методы управления очередью -
get() - get () удаляет и возвращает элемент из очереди.
put() - Помещение добавляет элемент в очередь.
qsize() - qsize () возвращает количество элементов, которые в настоящее время находятся в очереди.
empty()- empty () возвращает True, если очередь пуста; в противном случае - False.
full()- full () возвращает True, если очередь заполнена; в противном случае - False.
пример
#!/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")
Вывод
Когда приведенный выше код выполняется, он дает следующий результат:
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