Мультипроцессирование multiprocessing это режим работы

В этой статье мы коротко разберем такие библиотеки, как asyncio, threading, multiprocessing, коротко о них и как работают.
article placeholder

В этой статье мы коротко разберем такие библиотеки, как asyncio, threading, multiprocessing, коротко о них и как работают.

прОцесс пошел!

1.Asyncio

asyncio — библиотека, которая работает в режиме цикла событий с незаблокированным API,все события работают в 1 потоке в асинхронном режиме.

asyncio выполняет задачи в однопоточно-асинхронном режиме, если где-то наступает момент ожидания, программа переключается на другую задачу, чтобы потом вернуться обратно к моменту, где было ожидание, НО, это переключение произойдет только когда выполниться та задача, на которую программа переключилась в момент ожидания.

Приведу визуальный пример, а потом мы его рассмотрим в коде

В данном примере Иван это функция ivan, а машина это функция car, которые выполняют свои задачи

import asyncio

async def ivan():
print(» (1) // Иван идет по дороге и тут светофор! надо подождать…»)
await asyncio.sleep(2)
print(» (2) // Загорается зеленый свет для пешеходов и мы идем дальше»)

async def car():
print(» (3) // Машина начинает ехать»)
await asyncio.sleep(2)
print(» (4) // Загорается красный для пешеходов и снова начинают ехать машины»)

if __name__ == «__main__»:
event_l = asyncio.get_event_loop()
tasks = [event_l.create_task(ivan()), event_l.create_task(car())]
wait_tasks = asyncio.wait(tasks)
event_l.run_until_complete(wait_tasks)
event_l.close()

1) Выполняем часть задачи в функции ivan и начинаем ждать «что-то»

2) Пока ожидается «что-то» в функции ivan, происходит переход к выполнению задачи в функции car, тут выполняется часть задачи и мы начинаем ждать там «что-то», в этот момент происходит переход обратно к функции ivan

3)Так как мы перестали ждать «что-то» в функции ivan, завершаем задачу, пока функция car ждала «что-то», задача в функции ivan завершилась.

4) Функция car перестает ждать «что-то», и до конца выполняется задача

2.Threading

threading — библиотека, которая позволяет работать с несколькими потоками(количество ограничено только возможностями вашей машины).

Здесь нет асинхронной работы, просто каждая задача запускается в отдельном потоке, где все потоки работают параллельно(почти)

Упс… упираемся в GIL при многопоточной работе… как же так ?!

Сколько бы вы не создавали потоков в Python, все это будет скомкано в GIL, где в реальном времени может работать только 1 поток, просто во время работы программы будет происходить переключение между этими потоками.

Приведу визуальный пример

Как вы видите, в реальном времени может работать только 1 поток, просто GIL переключается между ними в зависимости от того какому потоку сейчас нужен GIL, но вопрос приоритетности оставим на потом.

Тем самым возникает вопрос, ускоряет ли многопоточность в Python работу кода ? — Нет, если не во всех, то в большинстве случаев замедляет.

Возникает второй вопрос, зачем тогда нужна многопоточность в Python ? — Если нужно например выполнение нескольких целей параллельно, то threading для этого отлично подходит, в отличие от multiprocessing.

Запускаем задачу в разных потоках вот так:

from threading import Thread

def task_1(a: int):
b = a * a
c = b + 400
d = 0
for i in range(600):
d += i
d += c
print(f»Результат вычислений: {d}»)

def task_2(a: int):
print(f»Вы ввели {a}»)

if __name__ == «__main__»:
t1 = Thread(target=task_1, args=(100,)) # Вводим параметры
t2 = Thread(target=task_2, args=(818,)) # Вводим параметры
t1.start() # Запускаем поток
t2.start() # Запускаем поток
t1.join() # Закрываем поток
t2.join() # Закрываем поток

3. Multiprocessing

multiprocessing — библиотека, которая позволяет работать в многопроцессорном режиме.

В отличие от библиотеки threading, multiprocessing ускоряет работу Python кода, но в урон большего потребления ресурсов.

Почему многопроцессорность позволяет ускорить работу Python, а многопоточность нет ?

Когда вы запускаете многопроцессорный код, каждый процесс имеет свои ресурсы и свой GIL, именно из-за этого происходит большее потребление ресурсов.

Когда вы запускаете многопоточный код, все потоки скомкиваются в 1 GIL, где просто происходит переключение между ними.

Каждый процесс работает только с теми ресурсами, которые вы ему предоставите, например если вы передали в 1 процесс одни параметры а во 2 процесс другие, они смогут этими ресурсами обменяться только через канал, и именно поэтому если цель — параллельная работа, лучше использовать threading, чтобы не писать лишний код для обмена данных между процессами.

Приведу визуальный пример работы многопроцессорности

Как вы видите, при запуска каждого процесса запускается свой GIL, что позволяет каждому процессу занимать весь GIL и ни с кем не делиться ;)

Тестим скорость работы в многопроцессорном режиме и однопроцессорном режиме —

код:

import datetime
from multiprocessing import Process

def test(a: int):
while a > 0:
a -= 1

if __name__ == «__main__»:
p = Process(target=test, args=(100000000,)) # Вводим параметры
p2 = Process(target=test, args=(100000000,)) # Вводим параметры
p3 = Process(target=test, args=(100000000,)) # Вводим параметры
print(«Старт на многопроцессорности: «, datetime.datetime.now().replace(microsecond=0))
p.start() # Запускаем процесс
p2.start() # Запускаем процесс
p3.start() # Запускаем процесс

p.join() # Закрываем процесс
p2.join() # Закрываем процесс
p3.join() # Закрываем процесс
print(«Конец на многопроцессорности: «, datetime.datetime.now().replace(microsecond=0))

# print(«Старт на одном процессе: «, datetime.datetime.now().replace(microsecond=0))
# test(100000000)
# test(100000000)
# test(100000000)
# print(«Конец на одном процессе: «, datetime.datetime.now().replace(microsecond=0))

результат:

При запуске текущего кода на нескольких процессах — код работает в 3 раза быстрее!

Как обмениваться данными между процессами ?

Есть три известных способа Queue, JoinableQueue и SimpleQueue, о них подробно тут —

Мы рассмотрим только 1 — Queue:

from multiprocessing import Process, Queue

def test(a: str, b: Queue):
print(f»Вы ввели: {a}»)
b.put(a)

def test_2(a: str, b: Queue):
print(f»Вы ввели: {a} и {b.get()}»)

if __name__ == «__main__»:
queue = Queue() # Создаем канал
p = Process(target=test, args=(«Привет», queue,)) # Вводим параметры
p2 = Process(target=test_2, args=(«Пончик», queue,)) # Вводим параметры

p.start() # Запускаем процесс
p2.start() # Запускаем процесс

p.join() # Закрываем процесс
p2.join() # Закрываем процесс

Мы создаем канал queue и с помощью него берем данные из одного процесса и передаем в другой.

Если моя статья кому-то пригодилось или внесла ясность в голову, для меня это лучший подарок, но от комментариев, подписки и кармы я не откажусь ;)

Всем удачи!

Параллельная обработка данных на разных ядрах процессора.

Пакет multiprocessing поддерживает порождение процессов с использованием API, аналогичного модулю threading.

Модуль многопроцессорной обработки данных предлагает как локальную, так и удаленную параллельную обработку данных, эффективно обходя GIL (глобальную блокировку интерпретатора) и используя ядра процессора вместо потоков. Благодаря этому, этот модуль позволяет программисту полностью использовать несколько процессоров на данной машине. Он работает как под Unix, так и под Windows.

Содержание:

  • Сходство и различия API модулей multiprocessing и threading.
  • Контексты и методы запуска процессов на разных ядрах.
  • Обмен данными между потоками ядер процессора.
  • Синхронизация между процессами на разных ядрах.
  • Совместное использование состояния между процессами.
  • Главные принципы программирования для модуля multiprocessing.

Сходство и различия API модулей multiprocessing и threading.

В модуле multiprocessing представлены API, не имеющие аналогов в модуле threading. Ярким примером этого является объект multiprocessing.Pool. Этот объект предлагает удобные средства параллельного выполнения функции для нескольких входных значений, автоматически распределяя их по ядрам процессора.

В следующем примере демонстрируется обычная практика определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример параллелизма данных с использованием пула ядер процессора.

import multiprocessing

def worker(x):
    name_proc = multiprocessing.current_process().name
    res = x*x
    print(name_proc, res)
    return res

data = range(3, 7)

with multiprocessing.Pool(2) as pool:
    print('Результаты:')
    print(pool.map(worker, data))
    
# Результаты:
# ForkPoolWorker-1 9 
# ForkPoolWorker-2 16
# ForkPoolWorker-1 25
# ForkPoolWorker-1 36
# [9, 16, 25, 36]

Аналогичный пример с использованием API, аналогичного модулю threading:

import multiprocessing

def worker(rear, write):
    while not read.empty():
        name_proc = multiprocessing.current_process().name
        x = read.get()
        res = x*x
        print(name_proc, res)
        write.put(res)
    else:
        read.close()
        write.close()
        
write = multiprocessing.Queue()
read = multiprocessing.Queue()
[read.put(x) for x in range(3, 7)]

NUM_CORE = 2
procs = []
for i in range(NUM_CORE):
    p = multiprocessing.Process(target=worker, args=(read, write,))    
    procs.append(p)
    p.start()

[proc.join() for proc in procs]
print([write.get() for _ in range(write.qsize())])

# Process-1 9
# Process-1 16
# Process-2 25
# Process-1 36
# [9, 16, 36, 25]

Контексты и методы запуска процессов на разных ядрах.

В зависимости от платформы модуль multiprocessing поддерживает три способа запуска процесса.

Методы запуска:

spawn:

Родительский процесс запускает новый процесс интерпретатора Python. Дочерний процесс унаследует только те ресурсы, которые необходимы для запуска метода Process.run() объекта multiprocessing.Process. В частности, ненужные файловые дескрипторы и дескрипторы родительского процесса не будут унаследованы. Запуск процесса с использованием этого метода довольно медленный по сравнению с использованием fork или forkserver.

Изменено в Python 3.8: В macOS метод запуска spawn теперь используется по умолчанию. Метод запуска fork следует считать небезопасным, так как он может привести к сбоям подпроцесса.

Доступно в Unix и Windows. По умолчанию в Windows и macOS.

fork:

Родительский процесс использует os.fork() для разветвления интерпретатора Python. Дочерний процесс, когда он начинается, фактически идентичен родительскому процессу. Все ресурсы родительского процесса наследуются дочерним процессом. Обратите внимание, что безопасное разветвление многопоточного процесса проблематично.

Доступно только в Unix. По умолчанию в Unix.

forkserver:

Когда программа запускается и выбирает метод запуска forkserver, запускается процесс сервера. С этого момента всякий раз, когда программе требуется новый процесс, родительский процесс подключается к серверу и запрашивает его разветвление для нового процесса. Процесс сервера является однопоточным, поэтому использование os.fork() безопасно. Никакие ненужные ресурсы не наследуются.

Доступно на платформах Unix, которые поддерживают передачу дескрипторов файлов по каналам Unix.

В Unix использование методов запуска spawn или forkserver также запускает процесс отслеживания ресурсов, который отслеживает несвязанные именованные системные ресурсы (такие как именованные семафоры или объекты разделяемой памяти), созданные процессами программы. Когда все процессы завершены, трекер ресурсов отсоединяет все оставшиеся отслеживаемые объекты. Обычно их не должно быть, но если процесс был остановлен сигналом, могут быть «утечки» ресурсов. Ни семафоры, ни сегменты разделяемой памяти не будут автоматически разъединены до следующей перезагрузки. Это проблематично для обоих объектов, поскольку система допускает только ограниченное количество именованных семафоров, а сегменты разделяемой памяти занимают некоторое пространство в основной памяти.

Чтобы выбрать метод запуска, используете функцию модуля multiprocessing.set_start_method() в предложении if __name__ == '__main__' основного модуля. Функция multiprocessing.set_start_method() не должна использоваться в программе более одного раза.

import multiprocessing

def worker(q):
    q.put('hello')

if __name__ == '__main__':
    multiprocessing.set_start_method('spawn')
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(q,))
    p.start()
    print(q.get())
    p.join()

В качестве альтернативы можно использовать функцию multiprocessing.get_context() для получения объекта контекста. Объекты контекста имеют тот же API, что и модуль multiprocessing, и позволяют использовать несколько методов запуска в одной программе.

import multiprocessing

def worker(q):
    q.put('hello')

if __name__ == '__main__':
    ctx = multiprocessing.get_context('spawn')
    q = ctx.Queue()
    p = ctx.Process(target=worker, args=(q,))
    p.start()
    print(q.get())
    p.join()

Обратите внимание, что объекты, относящиеся к одному контексту, могут быть несовместимы с процессами для другого контекста. В частности, блокировки, созданные с использованием контекста fork, не могут быть переданы процессам, запущенным с помощью методов запуска spawn или forkserver.

Библиотека, которая хочет использовать определенный метод запуска, вероятно, должна использовать get_context(), чтобы не мешать выбору пользователя библиотеки.

Предупреждение В настоящее время методы запуска spawn и forkserver не могут использоваться с «замороженными» исполняемыми файлами. То есть с двоичными файлами, созданными такими пакетами, как pyInstaller и cx_Freeze в Unix. Метод запуска fork работает с такими файлами нормально.

Обмен данными между потоками ядер процессора.

При использовании нескольких процессов обычно используется передача сообщений для связи между процессами и избегается необходимости использования каких-либо примитивов синхронизации, таких как блокировки.

Модуль multiprocessing поддерживает два типа каналов связи между процессами.

— Очереди Queues, в собственной реализации.

Класс multiprocessing.Queue является почти клоном класса queue.Queue. Очереди безопасны для потоков в разных ядрах процессора.

import multiprocessing

def worker(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(q,))
    p.start()
    print(q.get())
    p.join()

# "[42, None, 'hello']"

— Каналы Pipes.

Класс multiprocessing.Pipe() возвращает пару объектов, соединенных каналом, которые по умолчанию является duplex двусторонним.

from multiprocessing import

def worker(conn):
    conn.send([42, None, 'hello'])
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = multiprocessing.Pipe()
    p = multiprocessing.Process(target=worker, args=(child_conn,))
    p.start()
    print(parent_conn.recv())
    p.join()

# "[42, None, 'hello']"

Два объекта соединения, возвращаемые multiprocessing.Pipe(), представляют два конца канала. Каждый объект подключения имеет методы Pipe.send() — посылает данные в канал и Pipe.recv() — читает данные из канала.

Обратите внимание, что данные в канале могут быть повреждены, если два процесса или потока попытаются читать или записывать в один и тот же конец канала одновременно. Конечно, нет риска повреждения из-за процессов, использующих разные концы канала одновременно.

Синхронизация между процессами на разных ядрах.

Как правило, примитивы синхронизации не так необходимы в программе, использующей несколько ядер процессора, как в многопоточной.

Однако, модуль multiprocessing содержит эквиваленты всех примитивов синхронизации из модуля threading. Например, можно использовать блокировку Lock для обеспечения того, что только один процесс печатает на стандартный вывод за раз.

Без использования блокировки вывод различных процессов может все перемешать.

import multiprocessing

def worker(lock, i):
    lock.acquire()
    try:
        print('hello world', i)
    finally:
        lock.release()

if __name__ == '__main__':
    lock = multiprocessing.Lock()

    for num in range(10):
        multiprocessing.Process(target=worker, args=(lock, num)).start()

Совместное использование состояния между процессами.

Как упоминалось выше, при параллельном программировании обычно лучше избегать использования общих ресурсов, насколько это возможно. Это особенно верно при использовании нескольких ядер процессора.

Но если все-же действительно необходимо использование каких-то общих данных, то модуль multiprocessing предоставляет несколько способов сделать это.

— Использование общей памяти Shared memory.

Данные могут быть сохранены на карте общей памяти с помощью multiprocessing.Value или multiprocessing.Array.

import multiprocessing

def worker(num, arr):
    num.value = 3.1415927
    for i in range(len(arr)):
        arr[i] = -arr[i]

if __name__ == '__main__':
    num = multiprocessing.Value('d', 0.0)
    arr = multiprocessing.Array('i', range(10))

    p = multiprocessing.Process(target=worker, args=(num, arr))
    p.start()
    p.join()

    print(num.value)
    print(arr[:])

# 3.1415927
# [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

Аргументы 'd' и 'i', используемые при создании переменных num и arr, являются кодами типа, который используется модулем array: 'd' указывает на число с плавающей запятой двойной точности, а 'i' указывает на целое число со знаком. Эти общие объекты будут процессными и поточно-ориентированными.

Для большей гибкости в использовании разделяемой памяти можно использовать модуль multiprocessing.sharedctypes, который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.

— Использование серверного процесса Server process.

Объект SyncManager, возвращаемый multiprocessing.Manager(), управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси-объектов.

Например:

import multiprocessing

def worker(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()

if __name__ == '__main__':
    with multiprocessing.Manager() as manager:
        d = manager.dict()
        l = manager.list(range(10))

        p = multiprocessing.Process(target=worker, args=(d, l))
        p.start()
        p.join()

        print(d)
        print(l)

# {0.25: None, 1: '1', '2': 2}
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Менеджеры серверных процессов более гибкие, чем использование объектов общей памяти, т. к. могут быть созданы для поддержки произвольных типов объектов. Кроме того, один менеджер может использоваться совместно процессами на разных компьютерах в сети. Однако они медленнее, чем при использовании общей памяти.

Главные принципы программирования для модуля multiprocessing.

Существуют определенные правила и идиомы, которых следует придерживаться при использовании многопроцессорной обработки данных.

Следующие принципы относится ко всем методам запуска.

  • Избегайте общих ресурсов.
    Насколько это возможно, нужно стараться избегать перемещения больших объемов данных между процессами. Вероятно, лучше придерживаться использования очередей или каналов для связи между процессами, чем использовать примитивы синхронизации более низкого уровня.

  • Picklability.
    Убедитесь,что аргументы методов прокси-объектов являются упакованы модулем pickle.

  • Потоковая безопасность прокси.
    Не используйте прокси-объект из более чем одного потока, если вы не защитите его блокировкой. Никогда не возникает проблем с разными процессами, использующими один и тот же прокси.

  • Присоединение к зомби-процессам.
    В Unix, когда процесс завершается, но к нему не присоединяются, он становится зомби. Их никогда не должно быть очень много, потому что каждый раз, когда запускается новый процесс или вызывается active_children(), все завершенные процессы, которые еще не были присоединены, будут объединены. Также вызов метода Process.is_alive() завершенного процесса присоединится к процессу. Тем не менее, хорошей практикой является явное присоединение ко всем процессам, которые запускаются.

  • Лучше наследовать, чем pickle/unpickle.
    При использовании методов запуска spawn или forkserver многие типы из multiprocessing должны быть упакованы модулем pickle, чтобы дочерние процессы могли их использовать. Обычно следует избегать отправки общих объектов другим процессам с использованием каналов или очередей.

    В общем необходимо организовать программу так, чтобы процесс, которому требуется доступ к совместно используемому ресурсу, созданному где-то еще, мог унаследовать его от процесса-предка.

  • Избегайте завершения процессов.
    Использование метода Process.terminate() для остановки процесса может привести к тому, что любые общие ресурсы, такие как блокировки, семафоры, каналы и очереди, в настоящее время используемые процессом, станут сломанными или недоступными для других процессов. Поэтому, вероятно, лучше всего использовать этот метод только для процессов, которые никогда не используют общие ресурсы.

  • Присоединение к процессам, использующим очереди.
    Имейте в виду, что процесс, который поместил элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «питателя» в нижележащий канал. Дочерний процесс может вызвать метод очереди Queue.cancel_join_thread, чтобы избежать такого поведения.

    Это означает, что всякий раз, когда используется очередь, необходимо убедиться, что все элементы, помещенные в очередь, в конечном итоге будут удалены до присоединения к процессу. В противном случае, никто не может быть уверен, что процессы, поместившие элементы в очередь, завершатся. Помните также, что не демонические процессы будут присоединяться автоматически.

    Вот пример тупиковой ситуации:

    import multiprocessing
    
    def worker(q):
        q.put('X' * 1000000)
    
    if __name__ == '__main__':
        queue = multiprocessing.Queue()
        p = multiprocessing.Process(target=worker, args=(queue,))
        p.start()
        p.join()    # это тупик
        obj = queue.get()
    

    Что бы исправить ситуацию в примере выше, нужно поменять местами последние две строки или просто удалить строку p.join().

  • Явная передача ресурсов дочерним процессам.
    В Unix, использующем метод запуска fork, дочерний процесс может использовать общий ресурс, созданный в родительском процессе с использованием глобального ресурса. Лучше передать объект в качестве аргумента конструктору дочернего процесса.

    Помимо обеспечения совместимости кода (потенциально) с Windows и другими методами запуска, это также гарантирует, что, пока дочерний процесс все еще жив, объект не будет собираться сборщиком мусора в родительском процессе. Это может быть важно, если какой-то ресурс освобождается при сборке мусора в родительском процессе.

    Так например:

    import multiprocessing
    
    def worker():
        ... do something using "lock" ...
    
    if __name__ == '__main__':
        lock = multiprocessing.Lock()
        for i in range(10):
            multiprocessing.Process(target=worker).start()
    

    следует переписать как:

    import multiprocessing
    
    def worker(l):
        ... do something using "l" ...
    
    if __name__ == '__main__':
        lock = multiprocessing.Lock()
        for i in range(10):
            multiprocessing.Process(target=worker, args=(lock,)).start()
    
  • Остерегайтесь замены sys.stdin на файловый объект.
    Опасность заключается в том, что если несколько процессов вызовут file.close() для этого файлового объекта, то такое поведение может привести к тому, что одни и те же данные будут сброшены в него несколько раз, что приведет к повреждению.

Следующие принципы относится к методы запуска spawn и forkserver.

Есть несколько дополнительных ограничений, которые не применяются к методу запуска fork.

  • Больше picklability.
    Убедитесь, что все аргументы конструктора Process.__init__() являются picklable. Кроме того, если создается подкласс multiprocessing.Process(), необходимо убедится, что экземпляры будут picklable при вызове метода Process.start().

  • Глобальные переменные.
    Имейте в виду, если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если оно есть), может не совпадать со значением в родительском процессе во время вызова метода Process.start().

    Однако глобальные переменные, которые являются просто константами уровня модуля, не вызывают проблем.

  • Безопасный импорт основного модуля.
    Убедитесь, что основной модуль может быть безопасно импортирован новым интерпретатором Python, не вызывая нежелательных побочных эффектов, таких как запуск нового процесса.

    Например, при использовании метода запуска spawn или forkserver, выполняющего следующий модуль, произойдет сбой с исключением RuntimeError:

    from multiprocessing import Process
    
    def worker():
        print('hello')
    
    p = multiprocessing.Process(target=worker)
    p.start()
    

    Вместо этого следует защитить точку входа программы, используя if __name__ == '__main__':

    import multiprocessing
    
    def worker():
        print('hello')
    
    if __name__ == '__main__':
        multiprocessing.freeze_support()
        multiprocessing.set_start_method('spawn')
        p = multiprocessing.Process(target=worker)
        p.start()
    

    Строку multiprocessing.freeze_support() можно не указывать, если программа будет запускаться в обычном режиме, а не будет заморожена.

    Это позволяет вновь созданному интерпретатору Python безопасно импортировать модуль и затем запускать функцию модуля worker().

    Подобные ограничения применяются, если пул или менеджер создается в основном модуле.

В этой статье вы узнаете, как выполнять код параллельно с помощью модуля multiprocessing в Python.

Обычно программы имеют дело с двумя типами задач:

  • Связанные с вводом/выводом (I/O bound). Если задача выполняет много операций ввода/вывода, она называется задачей, связанной с вводом/выводом.
    Примеры: чтение из файлов, запись в файлы, подключение к базам данных и выполнение сетевого запроса.
    Ускорить выполнение можно с помощью многопоточности.
  • Привязанные к процессору (CPU bound). Когда задача выполняет много операций с использованием процессора, она называется задачей, привязанной к процессору.
    Примеры: изменение размера изображения и потоковое видео — это задачи, привязанные к процессору.
    Ускорить выполнение можно с помощью многопроцессорности.

Многопроцессорность позволяет двум или более процессорам одновременно обрабатывать две или более различных частей программы. В Python для реализации мультипроцессинга используется модуль multiprocessing.

Пример мультипроцессорной программы

import time

def task(n=100_000_000):
    while n:
        n -= 1

if __name__ == '__main__':
    start = time.perf_counter()
    task()
    task()
    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Вывод

Выполнение заняло 12.94 секунд.

Как это работает

1. Определим функцию task(), которая имеет большой цикл while от 10 миллионов до 0. Функция task() привязана к процессору, потому что она выполняет вычисления.

def task(n=100_000_000):
    while n:
        n -= 1

2.  Дважды вызовем функцию task() и засечем время обработки:

if __name__ == '__main__':
    start = time.perf_counter()
    task()
    task()
    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Используем модуль multiprocessing

В следующей программе используется модуль multiprocessing — и она выполняется быстрее:

import time
import multiprocessing

def task(n=100_000_000):
    while n:
        n -= 1

if __name__  ==  '__main__':
    start = time.perf_counter()

    p1 = multiprocessing.Process(target=task)
    p2 = multiprocessing.Process(target=task)

    p1.start()
    p2.start()

    p1.join()
    p2.join()

    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Вывод

Выполнение заняло 6.45 секунд.

Как это работает

1. Импортируем модуль multiprocessing.

import multiprocessing

2. Создадим два процесса и передадим функцию task() каждому из них:

p1 = multiprocessing.Process(target=task)
p2 = multiprocessing.Process(target=task)

Примечание. Конструктор Process() возвращает новый объект Process.

3. Вызовем метод start() объектов Process для запуска процесса:

p1.start()
p2.start()

4. Дожидаемся завершения процессов с помощью метода join():

p1.join()
p2.join()

Практический пример программы с многопроцессорностью

Мы будем использовать модуль multiprocessing для изменения размера изображений высокого разрешения.

Для начала установим библиотеку Pillow для обработки изображений:

pip install Pillow

Теперь напишем программу, которая создает миниатюры изображений в папке images и сохраняет их в папке thumbs:

import time
import os
from PIL import Image, ImageFilter

filenames = [
    'images/1.jpg',
    'images/2.jpg',
    'images/3.jpg',
    'images/4.jpg',
    'images/5.jpg',
]

def create_thumbnail(filename, size=(50,50), thumb_dir ='thumbs'):
    img = Image.open(filename)
    img = img.filter(ImageFilter.GaussianBlur())
    img.thumbnail(size)
    img.save(f'{thumb_dir}/{os.path.basename(filename)}')
    print(f'Файл {filename} обработан...')


if __name__ == '__main__':
    start = time.perf_counter()

    for filename in filenames:
        create_thumbnail(filename)
        
    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Вывод

Файл images/1.jpg обработан...
Файл images/2.jpg обработан...
Файл images/3.jpg обработан...
Файл images/4.jpg обработан...
Файл images/5.jpg обработан...
Выполнение заняло 1.28 секунд.

Теперь модифицируем программу для использования многопроцессорной обработки. Каждый процесс будет создавать миниатюру изображения:

import time
import os
import multiprocessing
from PIL import Image, ImageFilter


filenames = [
    'images/1.jpg',
    'images/2.jpg',
    'images/3.jpg',
    'images/4.jpg',
    'images/5.jpg',
]

def create_thumbnail(filename, size=(50,50),thumb_dir ='thumbs'):
    img = Image.open(filename)
    img = img.filter(ImageFilter.GaussianBlur())
    img.thumbnail(size)
    img.save(f'{thumb_dir}/{os.path.basename(filename)}')
    print(f'Файл {filename} обработан...')


if __name__ == '__main__':
    start = time.perf_counter()

    # создаем процесс
    processes = [multiprocessing.Process(target=create_thumbnail, args=[filename]) 
                for filename in filenames]

    # запускаем процесс
    for process in processes:
        process.start()

    # дожидаемся выполнение
    for process in processes:
        process.join()

    finish = time.perf_counter()

    print(f'Выполнение заняло {finish-start: .2f} секунд.')

Вывод

Файл images/5.jpg обработан...
Файл images/4.jpg обработан...
Файл images/1.jpg обработан...
Файл images/3.jpg обработан...
Файл images/2.jpg обработан...
Выполнение заняло 0.82 секунд.

При использовании многопроцессорности программа обрабатывает изображения гораздо быстрее.

Что нужно запомнить

Используйте многопроцессорную обработку Python для параллельного запуска кода для решения задач, привязанных к процессору.

Мультипрограммирование и мультипроцессирование

Мультипрограммирование
– одновременное выполнение нескольких
процессов на вычислительной машине с
одним процессором. В этом случае
распараллеливание процессов осуществляется
за счет того, что пока для одного процесса
выполняется операция ввода-вывода,
другой процесс использует время ЦП.

Мультипроцессирование
– одновременное выполнение нескольких
процессов на вычислительной машине,
имеющих несколько процессоров; если
количество одновременно выполняемых
процессов не превышает количества
процессоров, то все процессы будут
реально выполняться по времени, каждый
на своем процессоре. Если больше, то на
некоторых процессорах будет выполняться
мультипрограммирование. (рис)

При рассмотрении
вопросов мультипроцессирования часто
используют такие термины, как симметричный
и ассиметричный мультипроцесс
(применительно к аппаратуре и к организации
вычислительного процесса). Симметричная
мультипроцессорная архитектура
представляет из себя мультипроцессорную
систему с несколькими одинаковыми м/п.
Обычно такие м/п организованы в одном
корпусе. В случае ассиметричной
мультипроцессорной архитектуры
вычислительная система содержит
несколько различных процессоров,
выполняющих различные функции. В таких
системах 1 из процессоров управляет
работой остальных и называется ведущим,
все остальные – ведомыми.

При симметричной
организации вычислительного процесса
модули ОС могут выполняться на любом
из процессоров. (рис) При ассиметричной
организации вычислительного процесса
модули могут выполняться только на 1м
или на нескольких определенных
процессорах. При ассиметричной архитектуре
возможно только ассиметричная организация
вычислительного процесса, а при
симметричной архитектуре – как
симметричная, так и ассиметричная
организация вычислительного процесса.

Структуры ос

За 10летия развития
ОС наибольшую популярность получили
следующие структуры:

  1. Монолитные
    системы

Монолитная система
представляет из себя набор модулей,
которые в процессе функционирования
могут вызывать друг друга. Интерфейс
всех модулей строго определен. Монолитные
системы строятся путем компоновки
входящих в них модулей в 1 или несколько
исполняемых файлов. Например MS-DOS
представляет собой 2 выполняемых
монолитных файла: IO.com,
DOS.com.
1й определяет в себе модули физических
операций ввода-вывода, а 2й объединяет
логические функции ввода-вывода, те
функции работы на уровне файлов. Несмотря
на то, что монолитные системы представляют
из себя просто набор модулей, эти модули
все же несколько структурированы.
Например, все модули MS-DOS
структурированы в следующую структуру:
(рис)

Системные сервисы
реализуют функции, вызываемые пользователем
(считать сектор диска, записать), а
системные утилиты представляют из себя
модули, реализующие элементарные
операции, используемые системными
сервисами.

Вызов функции в
MS-DOS
выполняется следующим образом:

— в регистр AL
записывается код (номер) вызываемой
функции

— в другие регистры
заносятся параметры передаваемые этой
функцией

— после этого
вызывается программа прерывания Int
21h

— у этого прерывания
управление получает главная процедура
– она анализирует регистр AL
и вызывает соответствующую функцию
или системный сервис, передавая ей
параметры указанные в регистрах.

В некоторых других
монолитных системах параметры могут
передаваться не через регистры, а через
стек. В остальном принцип такой же.

Достоинства
монолитной системы: простота реализации

Недостатки:
сложность модификации (все модули
связаны – придется заново производить
перекомпоновку системы), очень сложная
реализация контроля доступа (защиты)

  1. Многоуровневая
    система

В многоуровневых
системах все модули ОС сгруппированы
в несколько иерархических уровней,
причем модули более высоких уровней
могут вызывать модули более низких
уровней только по строго определенным
правилам. Нижележащие уровни считаются
более привилегированными, а вышележащие
– менее.

1я многоуровневая
система была разработана в 1968г и
называлась THE.
В этой системе было реализовано 6 уровней:

5) системный оператор

4) прикладные
процессы

3) управление
вводом-выводом

2) консоль

1) управление
памятью

0) управление
процессами

На самом низком
привилегированном уровне выполнялось
управление процессами и обработка
прерываний. На 1м более высоком уровне
для процессов выделялась память, на 2м
уровне обеспечивался доступ к терминалу,
3й уровень управлял операциями
ввода-вывода, 4й – выполнением прикладных
программ, 5й обеспечивал управление
системой. Все эти 6 уровней ОС были
реализованы программно, поэтому не
обеспечивали полную защиту уровней
друг от друга. А в MULTICS
и Unix
уровни реализованы программно-аппаратно,
поэтому там защита и управление доступом
хорошо развиты.

  1. Системы виртуальных
    машин

1я машина была
разработана IBM
VM/370
(рис)

На аппаратуре
работает управляющая программа СР,
выполняющая действия:

  1. выполнение операций
    ввода-вывода на устройства

  2. обработка прерываний

  3. моделирование
    нескольких виртуальных машин, являющихся
    точной копией реальной аппаратуры

  4. периодическое
    выделение виртуальным машинам квантов
    времени ЦП, при этом сама СР работает
    в привилегированном режиме (может
    выполнять все операции), а виртуальные
    машины работают в пользовательском
    режиме. На каждой виртуальной машине
    работает либо пользовательская
    (однозадачная) ???, либо более мощная.

Как только ОС,
работающая на виртуальной машине,
попытается выполнить операцию ввода-вывода
или какую-либо другую привилегированную
операцию, то произойдет прерывание по
защите (т к они работают в пользовательском
режиме). СР перехватит прерывание,
определит, что нужно сделать, выполнит
требуемую операцию и возвратит результат
виртуальной машине.

Несколько другой
подход будет применен Intel
в своих м/п. м/п Intel
начиная с 80386 поддерживают режим так
называемого 8086. Это означает, что они
могут создавать в защищенном режиме
несколько параллельно работающих
виртуальных машин, моделирующих м/п
8086.

  1. Системы с
    экзоядром

Эти системы являются
частным случаем виртуальных машин, в
которых все физические ресурсы
вычислительной системы частями
закрепляются за разными виртуальными
машинами. (рис)

Это позволяет еще
больше функций перевести из
привилегированного режима в
пользовательский. Реализация становится
намного сложнее, следовательно системы
с экзоядром особого распространения
не получили.

  1. Система
    «клиент-сервер»

В системах
«клиент-сервер» большинство выполняемых
процессов как пользовательских, так и
системных подразделяется на 2 группы –
клиенты и серверы. Серверы – процессы,
которые предоставляют в использование
некоторые ресурсы или выполняют
запрашиваемые клиентами функции, а
клиенты – процессы, которые либо
потребляют ресурсы, либо посылают
запросы к серверу. И клиенты, и серверы
выполняются в пользовательском режиме
в соответствующих адресных пространствах,
и поэтому защищены друг от друга. (рис)

Клиенты не
взаимодействует непосредственно с
серверами, а посылают свои запросы ОС,
а ОС перенаправляет запрос соответствующему
серверу. Выполнив запрос, сервер
пересылает ответ также ОС, а она
перенаправляет его к соответствующему
клиенту. За счет того, что часть модулей
по работе с ресурсами перенесены на
более высокий уровень, ядро стало более
компактным. Поэтому в системах
«клиент-сервер» его часто называют
микроядром. Повышается надежность. Еще
одним преимуществом системы «клиент-сервер»
является возможность легкого переноса
этой архитектура на сетевую среду (может
выполняться на разных компьютерах сети,
также использует соответствующие вызовы
ядра).

Процессы
и потоки

Любая операционная
система должна учитывать используемые
ресурсы и проделанную работу. Особенно
это касается мультипрограммных
вычислительных систем. В качестве
единицы работы практически во всех ОС
используется процесс. Процесс- это
загруженная в память и выполняющаяся
программа. Одна и та же программа может
одновременно выполняться в рамках
нескольких процессов. Каждый процесс
при создании получает своё собственное
адресное пространство. Ему выделяются
такие ресурсы как файлы, устройства,
различные системные объекты. Каждому
процессу присваивается уникальный
идентификатор, называемый process
ID.
С помощью этого идентификатора система
различает процессы. С каждым процессом
связываются различные управляющие
структуры, с помощью которых ОС учитывает
состояние самого процесса и связанные
с ним устройства. Совокупность всех
управляющих структур, связанных с
процессом, называют контекстом процесса.
Когда ОС переходит с выполнения одного
процесса на другой, она сохраняет
контекст текущего процесса и восстанавливает
контекст следующего нового процесса.
Поэтому очень часто переключение
процессора с выполнения одного процесса
на другой называют переключением
контекста. Переключение контекста может
быть 2х типов:

  1. Принудительное
    переключение контекста, при котором
    ОС сама прекращает выполнение процесса

  2. Добровольное
    переключение контекста. Происходит
    когда выполняющийся процесс сам перешёл
    в ожидание.

Во многих ОС
существуют специальные команды и
функции, с помощью которых либо
пользователи, либо сами процессоры во
время своей работы могут запускать
новые процессы. В некоторых ОС, например
Unix,
между запущенными таким образом
процессами устанавливаются родственные
отношения, при этом запускающий процесс
принято считать родительским процессом,
а запускаемый -дочерним. Родители могут
поправлять выполнением своих дочерних
процессов. А дочерние процессы могут
наследовать от своих родителей такие
ресурсы, как открытые файлы и устройства.
В каждый конкретный момент времени
процесс может находиться в следующем
состоянии.

htmlconvd 4cEYnC html b8af2fb19d00f99b

В состоянии ГОТОВ,
объект полностью готов к выполнению и
ждёт, когда ОС ему выделит время
процессора. В качестве претендента на
следующее выполнение ОС просматривает
только готовые процессы. Сразу после
его создания процесс…….

После того как
процесс выбран для выполнения (на ЦП),
его контекст восстанавливается, и он
переводится в состояние АКТИВЕН. В
активном состоянии процесс находится
до тех пор, пока не произойдёт оно из
следующих событий:

  1. Истёк
    квант времени, выделенный процессу для
    выполнения. ОС
    приостанавливает выполнение процесса,
    его контекст сохраняется и он переводится
    назад в очередь, а на его место запускается
    следующий процесс.

  2. За
    время работы процесса в очереди готовых
    появился процесс с более высоким
    приоритетом. ОС
    приостановит активный процесс, сохранит
    его контекст и опять по переходу 2
    переведёт его в состояние ГОТОВ, а на
    место него запустит более приоритетный
    процесс.

  3. За
    время своего выполнения, активный
    процесс перешел в ожидание какого-либо
    ресурса или события. В этом случае его
    контекст также сохраняется и он по
    переходу 3 переводится в состояние
    БЛОКИРОВАН, а на место него запускается
    следующий процесс. Переход 3- добровольное
    переключение контекста (процесса).

  4. Во
    время своего выполнения процесс
    завершился (5й переход). После этого
    процесс уничтожается. В состоянии
    БЛОКИРОВАН процесс находится до тех
    пор, пока не освободится ожидаемый
    ресурс или не произойдёт ожидаемое им
    событие. В этом случае процесс из
    состояния БЛОКИРОВАН переводится в
    состояние ГОТОВ и снова может конкурировать
    с другими процессами за время ЦП.

Мы рассмотрели
выполнение процессов в многозадачной
системе, когда время центрального
процессора выделяется процессом. Многие
современные ОС являются многопоточными.
То есть позволяют распараллеливать
вычисления внутри одного процесса путём
параллельного выполнения различных
функций процесса. Каждая такая
параллельно-выполняемая функция процесса
называется потоком.
Таким образом в многопоточных системах
каждый процесс может содержать несколько
потоков и время центрального процессора
в многопоточных системах уже выделяется
не процессом, а потоком, независимо от
того, к какому процессу они принадлежат.
Все потоки одного процесса работают в
едином адресном пространстве процессора,
и им доступны все ресурсы процесса,
независимо от того, какой поток эти
ресурсы создал. В многопоточной системе
схема для состояния процесса полностью
соответствует состоянию потока. В
многопоточных системах как правило с
потоками связывают уровень их приоритета
и в соответствии с этим приоритетом
помещают в очередь готовых. ОС выбирает
на выполнение самый высокоприоритетный
готовый поток. В многопоточных системах
есть 2 способа реализации параллельных
вычислений:

htmlconvd 4cEYnC html 4a6f707367ea5fbc

Либо организовать
b
и с как отдельные процессы, в каждом из
которых будет по одному потоку, либо
организовать их в один процесс, но каждой
ветке b
и c
организовать свой поток.

С точки зрения
времени выполнения и минимизации
используемых ресурсов лучше 2й вариант-
реализация параллельных ветвей алгоритма,
потому что потоки работают в едином
адресном пространстве и переключение
между ними не вызывает переключение
контекста процесса. Но параллельные
ветки алгоритма никак не защищены друг
от друга. Если параллельные ветки
необходимо защищать друг от друга, то
их лучше организовывать в вид отдельных
процессов, тогда каждая ветка будет
работать в своём адресом пространстве.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Python — это язык программирования, который полезен при разработке веб-сайтов и приложений. Также занимается визуализацией и анализом данных. Это сравнительно простой язык, который часто используют даже непрограммисты. В этой статье говорится об использовании многопроцессорности в Python.

Содержание

  1. Multiprocessing
  2. Multiprocessing в Python
  3. Импортировать Multiprocessing
  4. Пример 1
  5. Пример 2
  6. Пример 3
  7. Пример 4
  8. Заключение

Multiprocessing

Многопроцессорность имеет дело с потенциалом системы, которая поддерживает более одного процессора одновременно. Все приложения в многопроцессорной обработке разбиты на небольшие шаблоны, и каждый из этих шаблонов работает самостоятельно. В этом типе процесса используются два или более процесса. Представим себе, что компьютер работает на одном процессоре. Если мы назначим несколько процессоров одновременно, то через короткое время он будет передавать переключение каждой задачи другой задаче, чтобы все процессы продолжали работать. Другими словами, если есть еще более сложные задачи, которые нужно выполнить, их будет поддерживать на пути к завершению. В то время как распределить равное время для каждой задачи — тоже большая проблема. В этом состоянии нам необходимо возникнуть понятие многопроцессорности. Многопроцессорная система может позволить.

  • Multiprocessing: компьютер, использующий более одного центрального процессора.
  • Многоядерный процессор: отдельный компонент, использующий два или более процессора.

Multiprocessing в Python

Используя концепцию многопроцессорности, центральный процессор может легко выполнять несколько задач одновременно. Python использует модуль многопроцессорности для введения параллельных кодов. Чтобы реализовать многопроцессорность, вам сначала нужно знать количество ядер в вашей системе. Мы использовали здесь один фрагмент кода, чтобы узнать о процессорах, работающих одновременно.

Для python вы можете использовать любой другой инструмент для выполнения кодов. Для этой цели мы использовали инструмент SPYDER, который обязательно выполнит коды языка Python.

Импортировать Multiprocessing

print(«Number of CPU : «, multiprocessing.cpu_count())

Во-первых, нам нужно импортировать пакет «pg» multiprocessing, который позволит работать. Для каждого ПК выходные данные могут отличаться. У меня количество ядер — 4.

Во-первых, нам нужно импортировать пакет «pg»

Пример 1

Используя модуль многопроцессорности, мы вычислим функции куба и квадрата.

Используя модуль многопроцессорности, мы вычислим функции куба и квадрата

Давайте разберемся с вышеупомянутым кодом. Мы создали процесс, а затем создали объект для обработки класса.

P1 = multiprocessing.process(print(print_cube(5, )))

P2= multiprocessing.process(print(print_square(2, )))

P1 и p2 — объекты процессов. Эти процессы содержат функциональный вызов с функцией печати, который напрямую вызывает функцию с аргументами в качестве параметра. Print_cube и print_square.

Мы используем методы start, чтобы запустить процесс.

P1.start()

P2.start()

Одна из используемых функций описывается как.

def print_square(num):

print(«Square: {}».format(num * num))

Функция square вычислит квадрат путем умножения заданного числа, использованного в вызове функции. Функция принимает число в переменной «num».

Функция square вычислит квадрат путем умножения заданного числа

Когда мы выполняем код, на выходе будут отображаться результирующие значения обеих функций.

Пример 2

Используя структуры данных, вы должны быть знакомы с использованием стеков и очередей в языке программирования. Из модуля многопроцессорности мы импортируем сюда класс очереди, который представляет собой структуру данных FIFO (First In First Out). Поочередно работают два процесса. Здесь мы используем две функции. Здесь функция Put () используется для вставки данных в очередь. В то время как мы используем get () для извлечения элементов из очереди. Ниже приведен код, иллюстрирующий работу многопроцессорной обработки в очереди. Первый шаг — ввести очереди.

Из очереди импорта в многопроцессорном режиме

После импорта модуля введите названия автомобилей в переменную массива

После импорта модуля введите названия автомобилей в переменную массива. Мы будем использовать цикл for для вставки значений в очередь, и put () сделает это. Переменная cnt используется для подсчета количества использованных автомобилей. Инициализируйте объект очереди с помощью оператора.

Queue = Queue()

Затем используется цикл для добавления значений; это будет использовать ключевое слово «queue», чтобы использовать функцию put () для аргументации имени автомобиля, который нужно вставить. Функция cnt будет увеличиваться после каждой вставки.

Для car in cars:

Queue.put(car)

Cnt +=1

Точно так же для put () используется цикл while, пока вся очередь не станет пустой.

Пока не queue.empty ():

Print(‘item no: ‘, cnt, ‘ ‘, queue.get())

Cnt+=1

Вывод показывает, что обе функции работают независимо

Вывод показывает, что обе функции работают независимо. Когда функция завершает свою работу, используется другой процесс.

Пример 3

Этот пример содержит два процесса. В каждом процессе «PID» получается с помощью функции get (). Мы использовали два модуля. Один — это многопроцессорность, а другой — операционная система.

В каждом процессе «PID» получается с помощью функции get ()

Каждому процессу присваивается свой идентификатор.

Def process ():

Print (“ID of process running worker1:{}”.format(os.getpid()))

Сообщение объединяется с вызовом функции для получения идентификатора процесса. В основной программе идентификатор основного процесса извлекается с использованием того же подхода через get (). Затем процессы создаются посредством многопроцессорности. Доступ к каждому процессу осуществляется через его соответствующие объекты. т.е. для процесса 1 объектом является P1.

P1 = multiprocessing.Process(target=process1)

Цель — это функция, которая должна выполняться процессом. По мере создания процессов их нужно запускать. Отображается статус каждого процесса; для этого нам нужно следить за завершением процесса. Потребуется некоторое время, чтобы подождать. Мы будем использовать функцию, которая будет ждать полного выполнения процесса.

P1.join ()

Когда эта функция соединения получает сигнал, она печатает заявление о том, что выполнение завершено. Логический флаг определит, активен ли процесс; если да, то это правда, иначе ложь.

Print(“process p1 is alive:{}”.format(p1.is_alive()))

Когда эта функция соединения получает сигнал

Теперь выполните программу и возьмите результат. Вы можете видеть, что сначала отображается основной идентификатор, а затем идентификатор каждого процесса. После завершения выполнения для обоих процессов отображается статус «false».

Теперь выполните программу и возьмите результат

Пример 4

В этом примере вы увидите работу multiprocessing и import time, pool, cpu_count. POOL используется для выполнения процессов с несколькими входами и распределения этих входных данных между процессами. Функция квадрата вычисляет квадрат входного значения. Используя модуль времени из пакета, мы будем использовать функцию сна.

Time.sleep(3)

Эта функция имеет расширение по времени на 3 секунды. Это означает, что значение этой функции будет показано по истечении указанного времени. В основной программе мы запускаем таймер, а затем выделяем фиктивные значения для каждых 4 ядер.

Эта функция имеет расширение по времени на 3 секунды

With pool() as pool

Res = pool.map(square, values)

Print(res)

Функция пула выдает значения и одновременно вызывает функцию. Затем нам нужно остановить таймер. Эта функция таймера будет считать секунды, использованные для этой функции, известные как прошедшее время; он получается вычитанием начального значения времени из конечного значения.

Теперь запустите программу. Отображается первая строка, а затем это занимает 3 секунды. После этого отображается другой результат.

После этого отображается другой результат

Заключение

«Пример многопроцессорной обработки в Python» — это краткое введение в феномен многопроцессорной обработки. Каждый описанный здесь пример импортирует разные пакеты из модуля многопроцессорности. Такая обработка процессов, параллельных друг другу, осуществляется за счет количества процессоров. Чем больше количество процессоров, тем больше шансов на хорошую многопроцессорность.

Понравилась статья? Поделить с друзьями:
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Мулинское рыбоводное хозяйство режим работы
  • Мультипрограммный режим работы это
  • Мулен руж саранск режим работы