Документация Python неофициальный перевод

multiprocessing.shared_memory.md

312 строк · 28.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.13/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.13/Lib/multiprocessing/shared_memory.py)1011Добавлено в версии 3.8.1213---1415Этот модуль предоставляет класс [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory) для выделения и управления разделяемой памятью, к которой могут обращаться один или несколько процессов на многоядерной или симметричной мультипроцессорной (SMP) машине. Для упрощения управления жизненным циклом разделяемой памяти, особенно между разными процессами, в модуле [`multiprocessing.managers`](https://python-all.ru/3.13/library/multiprocessing.html#module-multiprocessing.managers) также предоставляется подкласс [`BaseManager`](https://python-all.ru/3.13/library/multiprocessing.html#multiprocessing.managers.BaseManager) – [`SharedMemoryManager`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager).1617В этом модуле под разделяемой памятью понимаются блоки разделяемой памяти «в стиле POSIX» (хотя они не обязательно реализованы явно как таковые), а не «распределенная разделяемая память». Такой стиль разделяемой памяти позволяет разным процессам читать и записывать данные в общую (или разделяемую) область оперативной памяти. Обычно процессы ограничены доступом только к собственной памяти, но разделяемая память позволяет обмениваться данными между процессами, избегая необходимости отправлять сообщения с этими данными. Прямой обмен данными через память может дать значительный выигрыш в производительности по сравнению с обменом через диск, сокеты или другие механизмы, требующие сериализации/десериализации и копирования данных.1819#### `class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)`2021Создает экземпляр класса `SharedMemory` для создания нового блока разделяемой памяти или подключения к существующему. Каждый блок разделяемой памяти получает уникальное имя. Так один процесс может создать блок разделяемой памяти с определенным именем, а другой процесс – подключиться к тому же блоку, используя это же имя.2223Будучи ресурсом для обмена данными между процессами, блоки разделяемой памяти могут пережить породивший их процесс. Когда процесс больше не нуждается в доступе к блоку разделяемой памяти, который может по-прежнему требоваться другим процессам, следует вызвать метод [`close()`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.close). Когда блок разделяемой памяти больше не нужен ни одному процессу, для надлежащей очистки следует вызвать метод [`unlink()`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink).2425**Параметры:**2627- **name** ([*str*](https://python-all.ru/3.13/library/stdtypes.html#str) *|* *None*) – Уникальное имя запрашиваемой разделяемой памяти в виде строки. При создании нового блока разделяемой памяти, если для имени указано `None` (значение по умолчанию), будет сгенерировано новое имя.28- **create** ([*bool*](https://python-all.ru/3.13/library/functions.html#bool)) – Управляет тем, создается ли новый блок разделяемой памяти (`True`) или подключается существующий (`False`).29- **size** ([*int*](https://python-all.ru/3.13/library/functions.html#int)) – Запрашиваемое количество байтов при создании нового блока разделяемой памяти. Поскольку некоторые платформы выделяют память блоками, размер которых кратен размеру страницы памяти данной платформы, фактический размер блока разделяемой памяти может быть больше или равен запрошенному. При подключении к существующему блоку разделяемой памяти параметр *size* игнорируется.30- **track** ([*bool*](https://python-all.ru/3.13/library/functions.html#bool)) – Если указано `True`, блок разделяемой памяти регистрируется в процессе отслеживания ресурсов на тех платформах, где ОС не делает этого автоматически. Отслеживатель ресурсов гарантирует надлежащую очистку разделяемой памяти, даже если все остальные процессы, имеющие к ней доступ, завершаются, не сделав этого. Процессы Python, созданные от общего предка с помощью механизмов [`multiprocessing`](https://python-all.ru/3.13/library/multiprocessing.html#module-multiprocessing), совместно используют один процесс отслеживания ресурсов, и время жизни сегментов разделяемой памяти обрабатывается автоматически между этими процессами. Процессы Python, созданные иным способом, получают собственный отслеживатель ресурсов при доступе к разделяемой памяти с включенным параметром *track*. Это приведет к удалению разделяемой памяти отслеживателем ресурсов того процесса, который завершится первым. Чтобы избежать этой проблемы, пользователям [`subprocess`](https://python-all.ru/3.13/library/subprocess.html#module-subprocess) или отдельных процессов Python следует установить *track* в `False`, если уже есть другой процесс, выполняющий учет. Параметр *track* игнорируется в Windows, где есть собственный механизм отслеживания и разделяемая память автоматически удаляется при закрытии всех дескрипторов.3132Изменено в версии 3.13: Добавлен параметр *track*.3334#### `close()`3536Закрывает файловый дескриптор/дескриптор разделяемой памяти для данного экземпляра. `close()` следует вызывать, когда доступ к блоку разделяемой памяти из этого экземпляра больше не требуется. В зависимости от операционной системы, лежащая в основе память может быть освобождена или нет, даже если все дескрипторы на нее закрыты. Чтобы гарантировать надлежащую очистку, используйте метод [`unlink()`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink).3738#### `unlink()`3940Удаляет базовый блок разделяемой памяти. Этот метод следует вызывать только один раз на блок разделяемой памяти, независимо от количества дескрипторов на него, даже в других процессах. `unlink()` и [`close()`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.close) можно вызывать в любом порядке, но попытка доступа к данным внутри блока разделяемой памяти после `unlink()` может привести к ошибкам доступа к памяти в зависимости от платформы.4142Этот метод не действует в Windows, где единственный способ удалить блок разделяемой памяти – закрыть все дескрипторы.4344#### `buf`4546Представление памяти (memoryview) содержимого блока разделяемой памяти.4748#### `name`4950Доступ только для чтения к уникальному имени блока разделяемой памяти.5152#### `size`5354Доступ только для чтения к размеру блока разделяемой памяти в байтах.5556В следующем примере демонстрируется низкоуровневое использование экземпляров [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory):5758```python59>>> from multiprocessing import shared_memory60>>> shm_a = shared_memory.SharedMemory(create=True, size=10)61>>> type(shm_a.buf)62<class 'memoryview'>63>>> buffer = shm_a.buf64>>> len(buffer)651066>>> buffer[:4] = bytearray([22, 33, 44, 55])  # Изменить несколько за раз67>>> buffer[4] = 100                           # Изменить один байт за раз68>>> # Подключиться к существующему блоку разделяемой памяти69>>> shm_b = shared_memory.SharedMemory(shm_a.name)70>>> import array71>>> array.array('b', shm_b.buf[:5])  # Скопировать данные в новый array.array72array('b', [22, 33, 44, 55, 100])73>>> shm_b.buf[:5] = b'howdy'  # Изменить через shm_b, используя bytes74>>> bytes(shm_a.buf[:5])      # Доступ через shm_a75b'howdy'76>>> shm_b.close()   # Закрыть каждый экземпляр SharedMemory77>>> shm_a.close()78>>> shm_a.unlink()  # Вызвать unlink только один раз, чтобы освободить разделяемую память79```8081В следующем примере демонстрируется практическое применение класса [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory) с [массивами NumPy](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html) – доступ к одному и тому же `numpy.ndarray` из двух разных оболочек Python:8283```pycon84>>> # В первой интерактивной оболочке Python85>>> import numpy as np86>>> a = np.array([1, 1, 2, 3, 5, 8])  # Начать с существующего массива NumPy87>>> from multiprocessing import shared_memory88>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)89>>> # Теперь создать массив NumPy, поддерживаемый разделяемой памятью90>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)91>>> b[:] = a[:]  # Скопировать исходные данные в разделяемую память92>>> b93array([1, 1, 2, 3, 5, 8])94>>> type(b)95<class 'numpy.ndarray'>96>>> type(a)97<class 'numpy.ndarray'>98>>> shm.name  # Мы не указали имя, поэтому оно было выбрано автоматически99'psm_21467_46075'100101>>> # В той же оболочке или в новой оболочке Python на той же машине102>>> import numpy as np103>>> from multiprocessing import shared_memory104>>> # Подключиться к существующему блоку разделяемой памяти105>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')106>>> # Обратите внимание, что a.shape равно (6,), а a.dtype равно np.int64 в этом примере107>>> c = np.ndarray((6,), dtype=np.int64, buffer=existing_shm.buf)108>>> c109array([1, 1, 2, 3, 5, 8])110>>> c[-1] = 888111>>> c112array([  1,   1,   2,   3,   5, 888])113114>>> # Вернувшись в первую интерактивную оболочку Python, b отражает это изменение115>>> b116array([  1,   1,   2,   3,   5, 888])117118>>> # Очистка из второй оболочки Python119>>> del c  # Необязательно; просто подчёркивается, что массив больше не используется120>>> existing_shm.close()121122>>> # Очистка из первой оболочки Python123>>> del b  # Необязательно; просто подчёркивается, что массив больше не используется124>>> shm.close()125>>> shm.unlink()  # Освободить и удалить блок разделяемой памяти в самом конце126```127128#### `class multiprocessing.managers.SharedMemoryManager([address[, authkey]])`129130Подкласс [`multiprocessing.managers.BaseManager`](https://python-all.ru/3.13/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для управления блоками разделяемой памяти между процессами.131132Вызов [`start()`](https://python-all.ru/3.13/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) у экземпляра `SharedMemoryManager` запускает новый процесс. Единственная цель этого нового процесса – управлять жизненным циклом всех созданных через него блоков разделяемой памяти. Чтобы инициировать освобождение всех блоков разделяемой памяти, управляемых этим процессом, вызовите [`shutdown()`](https://python-all.ru/3.13/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown) у экземпляра. Это запускает вызов [`unlink()`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory.unlink) для всех объектов [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory), управляемых этим процессом, после чего завершает сам процесс. Создавая экземпляры `SharedMemory` через `SharedMemoryManager`, мы избегаем необходимости вручную отслеживать и инициировать освобождение ресурсов разделяемой памяти.133134Этот класс предоставляет методы для создания и возврата экземпляров [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory), а также для создания объекта, подобного списку ([`ShareableList`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.ShareableList)), основанного на разделяемой памяти.135136Обратитесь к [`BaseManager`](https://python-all.ru/3.13/library/multiprocessing.html#multiprocessing.managers.BaseManager) за описанием унаследованных необязательных входных аргументов *address* и *authkey*, а также за тем, как их можно использовать для подключения к существующей службе `SharedMemoryManager` из других процессов.137138#### `SharedMemory(size)`139140Создает и возвращает новый объект [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.SharedMemory) с указанным *size* в байтах.141142#### `ShareableList(sequence)`143144Создаёт и возвращает новый объект [`ShareableList`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager.ShareableList), инициализированный значениями из входной *последовательности*.145146Следующий пример демонстрирует основные механизмы [`SharedMemoryManager`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager):147148```pycon149>>> from multiprocessing.managers import SharedMemoryManager150>>> smm = SharedMemoryManager()151>>> smm.start()  # Запуск процесса, управляющего блоками разделяемой памяти152>>> sl = smm.ShareableList(range(4))153>>> sl154ShareableList([0, 1, 2, 3], name='psm_6572_7512')155>>> raw_shm = smm.SharedMemory(size=128)156>>> another_sl = smm.ShareableList('alpha')157>>> another_sl158ShareableList(['a', 'l', 'p', 'h', 'a'], name='psm_6572_12221')159>>> smm.shutdown()  # Вызывает unlink() для sl, raw_shm и another_sl160```161162Следующий пример иллюстрирует более удобный способ использования объектов [`SharedMemoryManager`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager) с помощью оператора [`with`](https://python-all.ru/3.13/reference/compound_stmts.html#with), гарантирующий освобождение всех блоков разделяемой памяти после того, как они больше не нужны:163164```pycon165>>> with SharedMemoryManager() as smm:166...     sl = smm.ShareableList(range(2000))167...     # Разделение работы между двумя процессами, сохранение частичных результатов в sl168...     p1 = Process(target=do_work, args=(sl, 0, 1000))169...     p2 = Process(target=do_work, args=(sl, 1000, 2000))170...     p1.start()171...     p2.start()  # multiprocessing.Pool может быть эффективнее172...     p1.join()173...     p2.join()   # Ожидание завершения всей работы в обоих процессах174...     total_result = sum(sl)  # Объединение частичных результатов из sl175```176177При использовании [`SharedMemoryManager`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.managers.SharedMemoryManager) в операторе [`with`](https://python-all.ru/3.13/reference/compound_stmts.html#with) все блоки разделяемой памяти, созданные с помощью этого менеджера, освобождаются по завершении выполнения блока кода оператора `with`.178179#### `class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)`180181Предоставляет изменяемый списокоподобный объект, в котором все хранимые значения размещаются в блоке разделяемой памяти. Это ограничивает тип хранимых значений следующими встроенными типами данных:182183- [`int`](https://python-all.ru/3.13/library/functions.html#int) (знаковое 64-битное)184- [`float`](https://python-all.ru/3.13/library/functions.html#float)185- [`bool`](https://python-all.ru/3.13/library/functions.html#bool)186- [`str`](https://python-all.ru/3.13/library/stdtypes.html#str) (менее 10 МБ каждый при кодировке UTF-8)187- [`bytes`](https://python-all.ru/3.13/library/stdtypes.html#bytes) (менее 10 МБ каждый)188- `None`189190Он также существенно отличается от встроенного типа [`list`](https://python-all.ru/3.13/library/stdtypes.html#list) тем, что общая длина таких списков не может изменяться (т.е. нет `append()`, `insert()` и т.д.) и не поддерживается динамическое создание новых экземпляров `ShareableList` через срезы.191192*sequence* используется для заполнения нового `ShareableList` значениями. Установите в `None`, чтобы вместо этого подключиться к уже существующему `ShareableList` по его уникальному имени разделяемой памяти.193194*name* – это уникальное имя запрашиваемой разделяемой памяти, как описано в определении [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory). При подключении к существующему `ShareableList` укажите уникальное имя его блока разделяемой памяти, оставив *sequence* равным `None`.195196> **Примечание**197>198> Известная проблема существует для значений [`bytes`](https://python-all.ru/3.13/library/stdtypes.html#bytes) и [`str`](https://python-all.ru/3.13/library/stdtypes.html#str). Если они заканчиваются `\x00` нулевыми байтами или символами, то при получении по индексу из `ShareableList` они могут быть *молчаливо удалены*. Такое `.rstrip(b'\x00')` поведение считается ошибкой и может быть исправлено в будущем. См. [gh-106939](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html).199200Для приложений, в которых проблема заключается в удалении завершающих нулевых символов методом rstripping, обойти это можно, всегда добавляя дополнительный ненулевой байт в конец таких значений при сохранении и удаляя его при чтении:201202```pycon203>>> from multiprocessing import shared_memory204>>> nul_bug_demo = shared_memory.ShareableList(['?\x00', b'\x03\x02\x01\x00\x00\x00'])205>>> nul_bug_demo[0]206'?'207>>> nul_bug_demo[1]208b'\x03\x02\x01'209>>> nul_bug_demo.shm.unlink()210>>> padded = shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07'])211>>> padded[0][:-1]212'?\x00'213>>> padded[1][:-1]214b'\x03\x02\x01\x00\x00\x00'215>>> padded.shm.unlink()216```217218#### `count(value)`219220Возвращает количество вхождений *value*.221222#### `index(value)`223224Возвращает индекс первого вхождения *value*. Возбуждает [`ValueError`](https://python-all.ru/3.13/library/exceptions.html#ValueError), если *value* отсутствует.225226#### `format`227228Атрибут только для чтения, содержащий формат упаковки [`struct`](https://python-all.ru/3.13/library/struct.html#module-struct), используемый всеми текущими хранимыми значениями.229230#### `shm`231232Экземпляр [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory), в котором хранятся значения.233234Следующий пример демонстрирует базовое использование экземпляра [`ShareableList`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList):235236```python237>>> from multiprocessing import shared_memory238>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])239>>> [ type(entry) for entry in a ]240[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]241>>> a[2]242-273.154243>>> a[2] = -78.5244>>> a[2]245-78.5246>>> a[2] = 'dry ice'  # Изменение типов данных также поддерживается247>>> a[2]248'dry ice'249>>> a[2] = 'larger than previously allocated storage space'250Traceback (most recent call last):251  ...252ValueError: exceeds available storage for existing str253>>> a[2]254'dry ice'255>>> len(a)2567257>>> a.index(42)2586259>>> a.count(b'howdy')2600261>>> a.count(b'HoWdY')2621263>>> a.shm.close()264>>> a.shm.unlink()265>>> del a  # Использование ShareableList после вызова unlink() не поддерживается266```267268Следующий пример показывает, как один, два или много процессов могут получить доступ к одному и тому же [`ShareableList`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList), указав имя лежащего в его основе блока разделяемой памяти:269270```python271>>> b = shared_memory.ShareableList(range(5))         # В первом процессе272>>> c = shared_memory.ShareableList(name=b.shm.name)  # Во втором процессе273>>> c274ShareableList([0, 1, 2, 3, 4], name='...')275>>> c[-1] = -999276>>> b[-1]277-999278>>> b.shm.close()279>>> c.shm.close()280>>> c.shm.unlink()281```282283Следующий пример демонстрирует, что объекты [`ShareableList`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList) (и лежащие в их основе [`SharedMemory`](https://python-all.ru/3.13/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory)) могут быть сериализованы и десериализованы при необходимости. Обратите внимание, что это всё равно будет тот же самый общий объект. Это происходит потому, что десериализованный объект имеет то же уникальное имя и просто подключается к существующему объекту с тем же именем (если объект ещё жив):284285```python286>>> import pickle287>>> from multiprocessing import shared_memory288>>> sl = shared_memory.ShareableList(range(10))289>>> list(sl)290[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]291```292293```python294>>> deserialized_sl = pickle.loads(pickle.dumps(sl))295>>> list(deserialized_sl)296[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]297```298299```python300>>> sl[0] = -1301>>> deserialized_sl[1] = -2302>>> list(sl)303[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]304>>> list(deserialized_sl)305[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]306```307308```python309>>> sl.shm.close()310>>> sl.shm.unlink()311```312