В нашем предыдущем уроке мы узнали о Примере CSV на Python. В этом уроке мы собираемся изучить Многозадачность на Python на примерах.
Многозадачность на Python
Параллельная обработка привлекает все больше внимания в настоящее время. Если вы до сих пор не знаете о параллельной обработке, изучите из Википедии. Поскольку производители ЦП начинают добавлять всё больше ядер к своим процессорам, создание параллельного кода – отличный способ улучшить производительность. Python представил модуль multiprocessing, который позволяет нам писать параллельный код. Чтобы понять основные мотивы этого модуля, мы должны знать некоторые основы параллельного программирования. После прочтения этой статьи мы надеемся, что вы сможете узнать несколько вещей на эту тему.
Многозадачность на Python: Процесс, Очередь и Блокировки
Есть много классов в модуле многопроцессорности Python для создания параллельных программ. Среди них три основных класса: Process
, Queue
и Lock
. Эти классы помогут вам создать параллельную программу. Но прежде чем описывать их, давайте начнем эту тему с простого кода. Чтобы сделать параллельную программу полезной, вам нужно знать, сколько ядер есть на вашем компьютере. Модуль многопроцессорности Python позволяет вам это узнать. Следующий простой код выведет количество ядер на вашем компьютере.
import multiprocessing
print("Number of cpu : ", multiprocessing.cpu_count())
Следующий вывод может отличаться для вашего компьютера. Для меня количество ядер составляет 8.
Класс Process многопроцессорности Python
Класс Process
в многопроцессорном модуле Python является абстракцией, которая настраивает другой процесс Python, предоставляет ему код для выполнения и способ для родительского приложения контролировать выполнение. Есть две важные функции, принадлежащие классу Process – функции start()
и join()
. Сначала нам нужно написать функцию, которая будет запущена процессом. Затем нам нужно создать объект процесса. Если мы создаем объект процесса, ничего не произойдет, пока мы не скажем ему начать обработку с помощью функции start()
. Затем процесс будет запущен и вернет свой результат. После этого мы указываем процессу завершиться с помощью функции join()
. Без вызова функции join()
процесс останется бездействующим и не завершится. Поэтому, если вы создаете много процессов и не завершаете их, вы можете столкнуться с нехваткой ресурсов. Затем вам придется убивать их вручную. Одна важная вещь: если вы хотите передать какой-либо аргумент через процесс, вам нужно использовать ключевой аргумент args
. Следующий код поможет понять использование класса Process.
from multiprocessing import Process
def print_func(continent='Asia'):
print('The name of continent is : ', continent)
if __name__ == "__main__": # confirms that the code is under main function
names = ['America', 'Europe', 'Africa']
procs = []
proc = Process(target=print_func) # instantiating without any argument
procs.append(proc)
proc.start()
# создание объекта процесса с аргументами
for name in names:
# print(name)
proc = Process(target=print_func, args=(name,))
procs.append(proc)
proc.start()
# завершение процессов
for proc in procs:
proc.join()
Выходной результат следующего кода будет:
Класс очереди многопроцессорности Python
У вас есть базовые знания о структуре данных компьютера, вероятно, вы знаете о очереди. Модули многопроцессорности Python предоставляют класс Queue
, который является именно структурой данных First-In-First-Out. Они могут хранить любой объект Python pickle (хотя простые объекты предпочтительнее) и чрезвычайно полезны для обмена данными между процессами. Очереди особенно полезны, когда передаются в качестве параметра в функцию цели процесса, чтобы позволить процессу потреблять данные. С помощью функции put()
мы можем вставлять данные в очередь, а с помощью get()
мы можем получать элементы из очередей. Вот пример кода:
from multiprocessing import Queue
colors = ['red', 'green', 'blue', 'black']
cnt = 1
# создание объекта очереди
queue = Queue()
print('pushing items to queue:')
for color in colors:
print('item no: ', cnt, ' ', color)
queue.put(color)
cnt += 1
print('\npopping items from queue:')
cnt = 0
while not queue.empty():
print('item no: ', cnt, ' ', queue.get())
cnt += 1
Класс блокировки многопроцессорности Python
Задача класса Lock довольно проста. Он позволяет коду захватывать блокировку, чтобы ни один другой процесс не мог выполнять аналогичный код, пока блокировка не будет освобождена. Таким образом, задача класса Lock в основном состоит из двух частей. Одна из них – захват блокировки, а другая – освобождение блокировки. Для захвата блокировки используется функция acquire()
, а для освобождения блокировки – функция release()
.
Пример многопроцессорности Python
В этом примере многопроцессорности Python мы объединим всё наше знание вместе. Предположим, у нас есть несколько задач для выполнения. Чтобы выполнить эту задачу, мы будем использовать несколько процессов. Итак, мы будем поддерживать две очереди. Одна будет содержать задачи, а другая будет содержать журнал выполненных задач. Затем мы создаем экземпляры процессов для выполнения задачи. Обратите внимание, что класс очереди Python уже синхронизирован. Это означает, что нам не нужно использовать класс Lock для блокировки доступа нескольких процессов к одному и тому же объекту очереди. Поэтому в этом случае нам не нужно использовать класс Lock. Ниже приведена реализация, где мы добавляем задачи в очередь, затем создаем процессы и запускаем их, затем используем join()
для завершения процессов. Наконец, мы печатаем журнал из второй очереди.
from multiprocessing import Lock, Process, Queue, current_process
import time
import queue # imported for using queue.Empty exception
def do_job(tasks_to_accomplish, tasks_that_are_done):
while True:
try:
'''
try to get task from the queue. get_nowait() function will
raise queue.Empty exception if the queue is empty.
queue(False) function would do the same task also.
'''
task = tasks_to_accomplish.get_nowait()
except queue.Empty:
break
else:
'''
if no exception has been raised, add the task completion
message to task_that_are_done queue
'''
print(task)
tasks_that_are_done.put(task + ' is done by ' + current_process().name)
time.sleep(.5)
return True
def main():
number_of_task = 10
number_of_processes = 4
tasks_to_accomplish = Queue()
tasks_that_are_done = Queue()
processes = []
for i in range(number_of_task):
tasks_to_accomplish.put("Task no " + str(i))
# создание процессов
for w in range(number_of_processes):
p = Process(target=do_job, args=(tasks_to_accomplish, tasks_that_are_done))
processes.append(p)
p.start()
# завершение процесса
for p in processes:
p.join()
# печать вывода
while not tasks_that_are_done.empty():
print(tasks_that_are_done.get())
return True
if __name__ == '__main__':
main()
В зависимости от количества задач код займет некоторое время, чтобы показать вам результат. Результат следующего кода будет меняться от времени к времени.
Python multiprocessing Pool
Python multiprocessing Pool можно использовать для параллельного выполнения функции по нескольким входным значениям, распределение входных данных по процессам (параллелизм данных). Ниже приведен простой пример использования Python multiprocessing Pool.
from multiprocessing import Pool
import time
work = (["A", 5], ["B", 2], ["C", 1], ["D", 3])
def work_log(work_data):
print(" Process %s waiting %s seconds" % (work_data[0], work_data[1]))
time.sleep(int(work_data[1]))
print(" Process %s Finished." % work_data[0])
def pool_handler():
p = Pool(2)
p.map(work_log, work)
if __name__ == '__main__':
pool_handler()
Ниже приведено изображение вывода вышеуказанной программы. Обратите внимание, что размер пула равен 2, поэтому две функции work_log
выполняются параллельно. Когда одно из выполнений функции завершается, оно берет следующий аргумент и так далее. Вот и все, что касается модуля многопроцессорности в Python. Ссылка: Официальная документация
Source:
https://www.digitalocean.com/community/tutorials/python-multiprocessing-example