multiprocessing.shared_memory.md
1> **Источник:** https://python-all.ru/3.12/library/multiprocessing.shared_memory.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `multiprocessing.shared_memory` – Разделяемая память для прямого доступа между процессами89**Исходный код:** [Lib/multiprocessing/shared\_memory.py](https://python-all.ru/src/3.12/Lib/multiprocessing/shared_memory.py)1011Добавлено в версии 3.8.1213---1415Этот модуль предоставляет класс [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory) для выделения и управления разделяемой памятью, к которой могут обращаться один или несколько процессов на многоядерной или симметричной мультипроцессорной (SMP) машине. Для упрощения управления жизненным циклом разделяемой памяти, особенно между разными процессами, в модуле [`multiprocessing.managers`](https://python-all.ru/3.12/library/multiprocessing.html#module-multiprocessing.managers) также предоставляется подкласс [`BaseManager`](https://python-all.ru/3.12/library/multiprocessing.html#multiprocessing.managers.BaseManager) – [`SharedMemoryManager`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager).1617В этом модуле под разделяемой памятью понимаются блоки разделяемой памяти «в стиле POSIX» (хотя они не обязательно реализованы явно как таковые), а не «распределенная разделяемая память». Такой стиль разделяемой памяти позволяет разным процессам читать и записывать данные в общую (или разделяемую) область оперативной памяти. Обычно процессы ограничены доступом только к собственной памяти, но разделяемая память позволяет обмениваться данными между процессами, избегая необходимости отправлять сообщения с этими данными. Прямой обмен данными через память может дать значительный выигрыш в производительности по сравнению с обменом через диск, сокеты или другие механизмы, требующие сериализации/десериализации и копирования данных.1819#### `class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0)`2021Создает экземпляр класса `SharedMemory` для создания нового блока разделяемой памяти или подключения к существующему. Каждый блок разделяемой памяти получает уникальное имя. Так один процесс может создать блок разделяемой памяти с определенным именем, а другой процесс – подключиться к тому же блоку, используя это же имя.2223Будучи ресурсом для обмена данными между процессами, блоки разделяемой памяти могут пережить породивший их процесс. Когда процесс больше не нуждается в доступе к блоку разделяемой памяти, который может по-прежнему требоваться другим процессам, следует вызвать метод [`close()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.close). Когда блок разделяемой памяти больше не нужен ни одному процессу, для надлежащей очистки следует вызвать метод [`unlink()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink).2425**Параметры:**2627- **name** ([*str*](https://python-all.ru/3.12/library/stdtypes.html#str) *|* *None*) – Уникальное имя запрашиваемой разделяемой памяти в виде строки. При создании нового блока разделяемой памяти, если для имени указано `None` (значение по умолчанию), будет сгенерировано новое имя.28- **create** ([*bool*](https://python-all.ru/3.12/library/functions.html#bool)) – Управляет тем, создается ли новый блок разделяемой памяти (`True`) или подключается существующий (`False`).29- **size** ([*int*](https://python-all.ru/3.12/library/functions.html#int)) – Запрашиваемое количество байтов при создании нового блока разделяемой памяти. Поскольку некоторые платформы выделяют память блоками, размер которых кратен размеру страницы памяти данной платформы, фактический размер блока разделяемой памяти может быть больше или равен запрошенному. При подключении к существующему блоку разделяемой памяти параметр *size* игнорируется.3031#### `close()`3233Закрывает доступ к разделяемой памяти из данного экземпляра. Для надлежащей очистки ресурсов каждый экземпляр должен вызвать [`close()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.close), когда он больше не нужен. Обратите внимание: вызов `close()` не приводит к уничтожению самого блока разделяемой памяти.3435#### `unlink()`3637Запрашивает уничтожение базового блока разделяемой памяти. Для надлежащей очистки ресурсов [`unlink()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink) должен быть вызван один раз (и только один раз) во всех процессах, которым нужен блок разделяемой памяти. После запроса на уничтожение блок разделяемой памяти может быть уничтожен немедленно или не сразу, и это поведение может различаться на разных платформах. Попытки доступа к данным внутри блока разделяемой памяти после вызова `unlink()` могут привести к ошибкам доступа к памяти. Примечание: последний процесс, освобождающий блок разделяемой памяти, может вызывать `unlink()` и [`close()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.close) в любом порядке.3839#### `buf`4041Представление памяти (memoryview) содержимого блока разделяемой памяти.4243#### `name`4445Доступ только для чтения к уникальному имени блока разделяемой памяти.4647#### `size`4849Доступ только для чтения к размеру блока разделяемой памяти в байтах.5051В следующем примере демонстрируется низкоуровневое использование экземпляров [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory):5253```python54>>> from multiprocessing import shared_memory55>>> shm_a = shared_memory.SharedMemory(create=True, size=10)56>>> type(shm_a.buf)57<class 'memoryview'>58>>> buffer = shm_a.buf59>>> len(buffer)601061>>> buffer[:4] = bytearray([22, 33, 44, 55]) # Изменить несколько за раз62>>> buffer[4] = 100 # Изменить один байт за раз63>>> # Подключиться к существующему блоку разделяемой памяти64>>> shm_b = shared_memory.SharedMemory(shm_a.name)65>>> import array66>>> array.array('b', shm_b.buf[:5]) # Скопировать данные в новый array.array67array('b', [22, 33, 44, 55, 100])68>>> shm_b.buf[:5] = b'howdy' # Изменить через shm_b, используя bytes69>>> bytes(shm_a.buf[:5]) # Доступ через shm_a70b'howdy'71>>> shm_b.close() # Закрыть каждый экземпляр SharedMemory72>>> shm_a.close()73>>> shm_a.unlink() # Вызвать unlink только один раз, чтобы освободить разделяемую память74```7576В следующем примере демонстрируется практическое применение класса [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory) с [массивами NumPy](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html) – доступ к одному и тому же `numpy.ndarray` из двух разных оболочек Python:7778```pycon79>>> # В первой интерактивной оболочке Python80>>> import numpy as np81>>> a = np.array([1, 1, 2, 3, 5, 8]) # Начать с существующего массива NumPy82>>> from multiprocessing import shared_memory83>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)84>>> # Теперь создать массив NumPy, поддерживаемый разделяемой памятью85>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)86>>> b[:] = a[:] # Скопировать исходные данные в разделяемую память87>>> b88array([1, 1, 2, 3, 5, 8])89>>> type(b)90<class 'numpy.ndarray'>91>>> type(a)92<class 'numpy.ndarray'>93>>> shm.name # Мы не указали имя, поэтому оно было выбрано автоматически94'psm_21467_46075'9596>>> # В той же оболочке или в новой оболочке Python на той же машине97>>> import numpy as np98>>> from multiprocessing import shared_memory99>>> # Подключиться к существующему блоку разделяемой памяти100>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')101>>> # Обратите внимание, что a.shape равно (6,), а a.dtype равно np.int64 в этом примере102>>> c = np.ndarray((6,), dtype=np.int64, buffer=existing_shm.buf)103>>> c104array([1, 1, 2, 3, 5, 8])105>>> c[-1] = 888106>>> c107array([ 1, 1, 2, 3, 5, 888])108109>>> # Вернувшись в первую интерактивную оболочку Python, b отражает это изменение110>>> b111array([ 1, 1, 2, 3, 5, 888])112113>>> # Очистка из второй оболочки Python114>>> del c # Необязательно; просто подчёркивается, что массив больше не используется115>>> existing_shm.close()116117>>> # Очистка из первой оболочки Python118>>> del b # Необязательно; просто подчёркивается, что массив больше не используется119>>> shm.close()120>>> shm.unlink() # Освободить и удалить блок разделяемой памяти в самом конце121```122123#### `class multiprocessing.managers.SharedMemoryManager([address[, authkey]])`124125Подкласс [`multiprocessing.managers.BaseManager`](https://python-all.ru/3.12/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для управления блоками разделяемой памяти между процессами.126127Вызов [`start()`](https://python-all.ru/3.12/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) у экземпляра `SharedMemoryManager` запускает новый процесс. Единственная цель этого нового процесса – управлять жизненным циклом всех созданных через него блоков разделяемой памяти. Чтобы инициировать освобождение всех блоков разделяемой памяти, управляемых этим процессом, вызовите [`shutdown()`](https://python-all.ru/3.12/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown) у экземпляра. Это запускает вызов [`unlink()`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink) для всех объектов [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory), управляемых этим процессом, после чего завершает сам процесс. Создавая экземпляры `SharedMemory` через `SharedMemoryManager`, мы избегаем необходимости вручную отслеживать и инициировать освобождение ресурсов разделяемой памяти.128129Этот класс предоставляет методы для создания и возврата экземпляров [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory), а также для создания объекта, подобного списку ([`ShareableList`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.ShareableList)), основанного на разделяемой памяти.130131Обратитесь к [`BaseManager`](https://python-all.ru/3.12/library/multiprocessing.html#multiprocessing.managers.BaseManager) за описанием унаследованных необязательных входных аргументов *address* и *authkey*, а также за тем, как их можно использовать для подключения к существующей службе `SharedMemoryManager` из других процессов.132133#### `SharedMemory(size)`134135Создает и возвращает новый объект [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory) с указанным *size* в байтах.136137#### `ShareableList(sequence)`138139Создаёт и возвращает новый объект [`ShareableList`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.ShareableList), инициализированный значениями из входной *последовательности*.140141Следующий пример демонстрирует основные механизмы [`SharedMemoryManager`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager):142143```pycon144>>> from multiprocessing.managers import SharedMemoryManager145>>> smm = SharedMemoryManager()146>>> smm.start() # Запуск процесса, управляющего блоками разделяемой памяти147>>> sl = smm.ShareableList(range(4))148>>> sl149ShareableList([0, 1, 2, 3], name='psm_6572_7512')150>>> raw_shm = smm.SharedMemory(size=128)151>>> another_sl = smm.ShareableList('alpha')152>>> another_sl153ShareableList(['a', 'l', 'p', 'h', 'a'], name='psm_6572_12221')154>>> smm.shutdown() # Вызывает unlink() для sl, raw_shm и another_sl155```156157Следующий пример иллюстрирует более удобный способ использования объектов [`SharedMemoryManager`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager) с помощью оператора [`with`](https://python-all.ru/3.12/reference/compound_stmts.html#with), гарантирующий освобождение всех блоков разделяемой памяти после того, как они больше не нужны:158159```pycon160>>> with SharedMemoryManager() as smm:161... sl = smm.ShareableList(range(2000))162... # Разделение работы между двумя процессами, сохранение частичных результатов в sl163... p1 = Process(target=do_work, args=(sl, 0, 1000))164... p2 = Process(target=do_work, args=(sl, 1000, 2000))165... p1.start()166... p2.start() # multiprocessing.Pool может быть эффективнее167... p1.join()168... p2.join() # Ожидание завершения всей работы в обоих процессах169... total_result = sum(sl) # Объединение частичных результатов из sl170```171172При использовании [`SharedMemoryManager`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager) в операторе [`with`](https://python-all.ru/3.12/reference/compound_stmts.html#with) все блоки разделяемой памяти, созданные с помощью этого менеджера, освобождаются по завершении выполнения блока кода оператора `with`.173174#### `class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)`175176Предоставляет изменяемый списокоподобный объект, в котором все хранимые значения размещаются в блоке разделяемой памяти. Это ограничивает тип хранимых значений следующими встроенными типами данных:177178- [`int`](https://python-all.ru/3.12/library/functions.html#int) (знаковое 64-битное)179- [`float`](https://python-all.ru/3.12/library/functions.html#float)180- [`bool`](https://python-all.ru/3.12/library/functions.html#bool)181- [`str`](https://python-all.ru/3.12/library/stdtypes.html#str) (менее 10 МБ каждый при кодировке UTF-8)182- [`bytes`](https://python-all.ru/3.12/library/stdtypes.html#bytes) (менее 10 МБ каждый)183- `None`184185Он также существенно отличается от встроенного типа [`list`](https://python-all.ru/3.12/library/stdtypes.html#list) тем, что общая длина таких списков не может изменяться (т.е. нет `append()`, `insert()` и т.д.) и не поддерживается динамическое создание новых экземпляров `ShareableList` через срезы.186187*sequence* используется для заполнения нового `ShareableList` значениями. Установите в `None`, чтобы вместо этого подключиться к уже существующему `ShareableList` по его уникальному имени разделяемой памяти.188189*name* – это уникальное имя запрашиваемой разделяемой памяти, как описано в определении [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory). При подключении к существующему `ShareableList` укажите уникальное имя его блока разделяемой памяти, оставив *sequence* равным `None`.190191> **Примечание**192>193> Известная проблема существует для значений [`bytes`](https://python-all.ru/3.12/library/stdtypes.html#bytes) и [`str`](https://python-all.ru/3.12/library/stdtypes.html#str). Если они заканчиваются `\x00` нулевыми байтами или символами, то при получении по индексу из `ShareableList` они могут быть *молчаливо удалены*. Такое `.rstrip(b'\x00')` поведение считается ошибкой и может быть исправлено в будущем. См. [gh-106939](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html).194195Для приложений, в которых проблема заключается в удалении завершающих нулевых символов методом rstripping, обойти это можно, всегда добавляя дополнительный ненулевой байт в конец таких значений при сохранении и удаляя его при чтении:196197```pycon198>>> from multiprocessing import shared_memory199>>> nul_bug_demo = shared_memory.ShareableList(['?\x00', b'\x03\x02\x01\x00\x00\x00'])200>>> nul_bug_demo[0]201'?'202>>> nul_bug_demo[1]203b'\x03\x02\x01'204>>> nul_bug_demo.shm.unlink()205>>> padded = shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07'])206>>> padded[0][:-1]207'?\x00'208>>> padded[1][:-1]209b'\x03\x02\x01\x00\x00\x00'210>>> padded.shm.unlink()211```212213#### `count(value)`214215Возвращает количество вхождений *value*.216217#### `index(value)`218219Возвращает индекс первого вхождения *value*. Возбуждает [`ValueError`](https://python-all.ru/3.12/library/exceptions.html#ValueError), если *value* отсутствует.220221#### `format`222223Атрибут только для чтения, содержащий формат упаковки [`struct`](https://python-all.ru/3.12/library/struct.html#module-struct), используемый всеми текущими хранимыми значениями.224225#### `shm`226227Экземпляр [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory), в котором хранятся значения.228229Следующий пример демонстрирует базовое использование экземпляра [`ShareableList`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList):230231```python232>>> from multiprocessing import shared_memory233>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])234>>> [ type(entry) for entry in a ]235[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]236>>> a[2]237-273.154238>>> a[2] = -78.5239>>> a[2]240-78.5241>>> a[2] = 'dry ice' # Изменение типов данных также поддерживается242>>> a[2]243'dry ice'244>>> a[2] = 'larger than previously allocated storage space'245Traceback (most recent call last):246 ...247ValueError: exceeds available storage for existing str248>>> a[2]249'dry ice'250>>> len(a)2517252>>> a.index(42)2536254>>> a.count(b'howdy')2550256>>> a.count(b'HoWdY')2571258>>> a.shm.close()259>>> a.shm.unlink()260>>> del a # Использование ShareableList после вызова unlink() не поддерживается261```262263Следующий пример показывает, как один, два или много процессов могут получить доступ к одному и тому же [`ShareableList`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList), указав имя лежащего в его основе блока разделяемой памяти:264265```python266>>> b = shared_memory.ShareableList(range(5)) # В первом процессе267>>> c = shared_memory.ShareableList(name=b.shm.name) # Во втором процессе268>>> c269ShareableList([0, 1, 2, 3, 4], name='...')270>>> c[-1] = -999271>>> b[-1]272-999273>>> b.shm.close()274>>> c.shm.close()275>>> c.shm.unlink()276```277278Следующий пример демонстрирует, что объекты [`ShareableList`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList) (и лежащие в их основе [`SharedMemory`](https://python-all.ru/3.12/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory)) могут быть сериализованы и десериализованы при необходимости. Обратите внимание, что это всё равно будет тот же самый общий объект. Это происходит потому, что десериализованный объект имеет то же уникальное имя и просто подключается к существующему объекту с тем же именем (если объект ещё жив):279280```python281>>> import pickle282>>> from multiprocessing import shared_memory283>>> sl = shared_memory.ShareableList(range(10))284>>> list(sl)285[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]286```287288```python289>>> deserialized_sl = pickle.loads(pickle.dumps(sl))290>>> list(deserialized_sl)291[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]292```293294```python295>>> sl[0] = -1296>>> deserialized_sl[1] = -2297>>> list(sl)298[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]299>>> list(deserialized_sl)300[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]301```302303```python304>>> sl.shm.close()305>>> sl.shm.unlink()306```307