threadsafety.md · Markdown – Документация Python
Документация Python неофициальный перевод

threadsafety.md

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

1> **Источник:** https://python-all.ru/3/library/threadsafety.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Гарантии потокобезопасности89На этой странице описаны гарантии потокобезопасности для встроенных типов в сборке Python со свободной потоковой обработкой (free-threaded build). Описанные здесь гарантии действуют при использовании Python с отключённым [GIL](https://python-all.ru/3/glossary.html#term-GIL) (режим свободной потоковой обработки). При включённом GIL большинство операций неявно сериализуются.1011Общие рекомендации по написанию потокобезопасного кода в свободно-поточной версии Python см. в разделе [Поддержка свободной потоковой обработки в Python](https://python-all.ru/3/howto/free-threading-python.html#freethreading-python-howto).1213## Уровни потокобезопасности1415В документации C API используются следующие уровни для описания гарантий потокобезопасности каждой функции. Уровни перечислены от наименее к наиболее безопасному.1617### Несовместимый1819Функция или операция, которую невозможно сделать безопасной для одновременного использования даже с внешней синхронизацией. Несовместимый код обычно обращается к глобальному состоянию несинхронизированным образом и должен вызываться только из одного потока в течение всего времени работы программы.2021Пример: функция, которая изменяет общепроцессное состояние, например обработчики сигналов или переменные окружения, где параллельные вызовы из любых потоков, даже с внешней блокировкой, могут конфликтовать со средой выполнения или другими библиотеками.2223### Совместимый2425Функция или операция, которую можно безопасно вызывать из нескольких потоков *при условии*, что вызывающая сторона обеспечивает соответствующую внешнюю синхронизацию, например, удерживая [блокировку](https://python-all.ru/3/glossary.html#term-lock) на время каждого вызова. Без такой синхронизации параллельные вызовы могут привести к [состояниям гонки](https://python-all.ru/3/glossary.html#term-race-condition) или [гонкам данных](https://python-all.ru/3/glossary.html#term-data-race).2627Пример: функция, которая читает или записывает данные объекта, внутреннее состояние которого не защищено блокировкой. Вызывающая сторона должна гарантировать, что никакие два потока не обращаются к одному и тому же объекту одновременно.2829### Безопасный на разных объектах3031Функция или операция, которую можно безопасно вызывать из нескольких потоков без внешней синхронизации, при условии, что каждый поток работает с **разным** объектом. Два потока могут вызывать функцию одновременно, но они не должны передавать один и тот же объект (или объекты, разделяющие общее внутреннее состояние) в качестве аргументов.3233Пример: функция, которая изменяет поля структуры с помощью неатомарных записей. Два потока могут безопасно вызывать функцию каждый на своём экземпляре структуры, но параллельные вызовы на *одном и том же* экземпляре требуют внешней синхронизации.3435### Безопасный на общих объектах3637Функция или операция, безопасная для одновременного использования на **одном и том же** объекте. Реализация использует внутреннюю синхронизацию (например, [поблочные блокировки](https://python-all.ru/3/glossary.html#term-per-object-lock) или [критические секции](https://python-all.ru/3/c-api/synchronization.html#python-critical-section-api)) для защиты общего изменяемого состояния, поэтому вызывающей стороне не нужно предоставлять собственную блокировку.3839Пример: [`PyList_GetItemRef()`](https://python-all.ru/3/c-api/list.html#c.PyList_GetItemRef) можно вызывать из нескольких потоков на одном и том же [`PyListObject`](https://python-all.ru/3/c-api/list.html#c.PyListObject) – он использует внутреннюю синхронизацию для сериализации доступа.4041### Атомарный4243Функция или операция, которая выглядит [атомарной](https://python-all.ru/3/glossary.html#term-atomic-operation) по отношению к другим потокам – она выполняется мгновенно с точки зрения других потоков. Это самая сильная форма потокобезопасности.4445Пример: [`PyMutex_IsLocked()`](https://python-all.ru/3/c-api/synchronization.html#c.PyMutex_IsLocked) выполняет атомарное чтение состояния мьютекса и может вызываться из любого потока в любое время.4647## Потокобезопасность для объектов списка4849Чтение одного элемента из [`list`](https://python-all.ru/3/library/stdtypes.html#list) является [атомарным](https://python-all.ru/3/glossary.html#term-atomic-operation):5051```python52lst[i]   # list.__getitem__53```5455Следующие методы обходят список и используют [атомарные](https://python-all.ru/3/glossary.html#term-atomic-operation) чтения каждого элемента для выполнения своей функции. Это означает, что они могут возвращать результаты, на которые повлияли параллельные изменения:5657```python58item in lst59lst.index(item)60lst.count(item)61```6263Все перечисленные выше операции не приобретают [поблочных блокировок](https://python-all.ru/3/glossary.html#term-per-object-lock). Они не блокируют параллельные изменения. Другие операции, удерживающие блокировку, не помешают этим операциям наблюдать промежуточные состояния.6465Все остальные операции, начиная с этого места, блокируются с помощью [поблочной блокировки](https://python-all.ru/3/glossary.html#term-per-object-lock).6667Запись одного элемента через `lst[i] = x` безопасна для вызова из нескольких потоков и не повредит список.6869Следующие операции возвращают новые объекты и выглядят [атомарными](https://python-all.ru/3/glossary.html#term-atomic-operation) для других потоков:7071```python72lst1 + lst2    # объединяет два списка в новый список73x * lst        # повторяет lst x раз в новом списке74lst.copy()     # возвращает поверхностную копию списка75```7677Следующие методы, которые работают только с одним элементом без необходимости сдвига, являются [атомарными](https://python-all.ru/3/glossary.html#term-atomic-operation):7879```python80lst.append(x)  # добавить в конец списка, сдвиг не требуется81lst.pop()      # извлечь элемент из конца списка, сдвиг не требуется82```8384Метод [`clear()`](https://python-all.ru/3/library/stdtypes.html#list.clear) также является [атомарным](https://python-all.ru/3/glossary.html#term-atomic-operation). Другие потоки не могут наблюдать удаление элементов.8586Метод [`sort()`](https://python-all.ru/3/library/stdtypes.html#list.sort) не является [атомарным](https://python-all.ru/3/glossary.html#term-atomic-operation). Другие потоки не могут наблюдать промежуточные состояния во время сортировки, но список выглядит пустым на время сортировки.8788Следующие операции могут позволить [безблокировочным](https://python-all.ru/3/glossary.html#term-lock-free) операциям наблюдать промежуточные состояния, поскольку они изменяют несколько элементов на месте:8990```python91lst.insert(idx, item)  # сдвигает элементы92lst.pop(idx)           # idx не в конце списка, сдвигает элементы93lst *= x               # копирует элементы на месте94```9596Метод [`remove()`](https://python-all.ru/3/library/stdtypes.html#list.remove) может допускать параллельные изменения, поскольку сравнение элементов может выполнять произвольный код Python (через [`__eq__()`](https://python-all.ru/3/reference/datamodel.html#object.__eq__)).9798[`extend()`](https://python-all.ru/3/library/stdtypes.html#list.extend) безопасно вызывать из нескольких потоков. Однако его гарантии зависят от переданного итерируемого объекта. Если это [`list`](https://python-all.ru/3/library/stdtypes.html#list), [`tuple`](https://python-all.ru/3/library/stdtypes.html#tuple), [`set`](https://python-all.ru/3/library/stdtypes.html#set), [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset), [`dict`](https://python-all.ru/3/library/stdtypes.html#dict) или [объект представления словаря](https://python-all.ru/3/library/stdtypes.html#dict-views) (но не их подклассы), то операция `extend` безопасна при параллельных изменениях итерируемого объекта. В противном случае создаётся итератор, который может быть параллельно изменён другим потоком. То же самое относится к конкатенации списка с другими итерируемыми объектами на месте при использовании `lst += iterable`.99100Аналогично, присваивание срезу списка с помощью `lst[i:j] = iterable` безопасно вызывать из нескольких потоков, но `iterable` блокируется только в том случае, если он также является [`list`](https://python-all.ru/3/library/stdtypes.html#list) (но не его подклассом).101102Операции, включающие множественные обращения, а также итерацию, никогда не являются атомарными. Например:103104```python105# не атомарно: чтение-изменение-запись106lst[i] = lst[i] + 1107108# не атомарно: проверка-затем-действие109if lst:110    item = lst.pop()111112# не потокобезопасно: итерация во время изменения113for item in lst:114    process(item)  # другой поток может изменить lst115```116117Рассмотрите внешнюю синхронизацию при совместном использовании экземпляров [`list`](https://python-all.ru/3/library/stdtypes.html#list) между потоками.118119## Потокобезопасность объектов dict120121Создание словаря с помощью конструктора [`dict`](https://python-all.ru/3/library/stdtypes.html#dict) является атомарным, когда его аргумент – это `dict` или [`tuple`](https://python-all.ru/3/library/stdtypes.html#tuple). При использовании метода [`dict.fromkeys()`](https://python-all.ru/3/library/stdtypes.html#dict.fromkeys) создание словаря атомарно, когда аргумент – `dict`, `tuple`, [`set`](https://python-all.ru/3/library/stdtypes.html#set) или [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset).122123Следующие операции и функции являются [безблокировочными](https://python-all.ru/3/glossary.html#term-lock-free) и [атомарными](https://python-all.ru/3/glossary.html#term-atomic-operation).124125```python126d[key]       # dict.__getitem__127d.get(key)   # dict.get128key in d     # dict.__contains__129len(d)       # dict.__len__130```131132Все остальные операции, начиная с этого момента, удерживают [блокировку объекта](https://python-all.ru/3/glossary.html#term-per-object-lock).133134Запись или удаление одного элемента безопасно вызывать из нескольких потоков, и это не повредит словарь:135136```python137d[key] = value        # записать138del d[key]            # удалить139d.pop(key)            # удалить и вернуть140d.popitem()           # удалить и вернуть последний элемент141d.setdefault(key, v)  # вставить, если отсутствует142```143144Эти операции могут сравнивать ключи с помощью [`__eq__()`](https://python-all.ru/3/reference/datamodel.html#object.__eq__), который может выполнять произвольный код Python. Во время таких сравнений словарь может быть изменён другим потоком. Для встроенных типов, таких как [`str`](https://python-all.ru/3/library/stdtypes.html#str), [`int`](https://python-all.ru/3/library/functions.html#int) и [`float`](https://python-all.ru/3/library/functions.html#float), которые реализуют `__eq__()` на C, базовая блокировка не освобождается во время сравнений, и это не вызывает проблем.145146Следующие операции возвращают новые объекты и удерживают [блокировку объекта](https://python-all.ru/3/glossary.html#term-per-object-lock) на время выполнения операции:147148```python149d.copy()      # возвращает поверхностную копию словаря150d | other     # объединяет два словаря в новый словарь151d.keys()      # возвращает новый объект представления dict_keys152d.values()    # возвращает новый объект представления dict_values153d.items()     # возвращает новый объект представления dict_items154```155156Метод [`clear()`](https://python-all.ru/3/library/stdtypes.html#dict.clear) удерживает блокировку на всё время своего выполнения. Другие потоки не могут наблюдать удаление элементов.157158Следующие операции блокируют оба словаря. Для [`update()`](https://python-all.ru/3/library/stdtypes.html#dict.update) и `|=` это применимо только в том случае, если другой операнд является [`dict`](https://python-all.ru/3/library/stdtypes.html#dict), который использует стандартный итератор dict (но не подклассы, переопределяющие итерацию). Для сравнения на равенство это относится к `dict` и его подклассам:159160```python161d.update(other_dict)  # оба заблокированы, если other_dict – это dict162d |= other_dict       # оба заблокированы, если other_dict – это dict163d == other_dict       # оба заблокированы для dict и его подклассов164```165166Все операции сравнения также сравнивают значения с помощью [`__eq__()`](https://python-all.ru/3/reference/datamodel.html#object.__eq__), поэтому для невстроенных типов блокировка может быть освобождена во время сравнения.167168[`fromkeys()`](https://python-all.ru/3/library/stdtypes.html#dict.fromkeys) блокирует как новый словарь, так и итерируемый объект, если итерируемый объект является именно [`dict`](https://python-all.ru/3/library/stdtypes.html#dict), [`set`](https://python-all.ru/3/library/stdtypes.html#set) или [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset) (не подклассами):169170```python171dict.fromkeys(a_dict)      # блокирует оба172dict.fromkeys(a_set)       # блокирует оба173dict.fromkeys(a_frozenset) # блокирует оба174```175176При обновлении из итерируемого объекта, не являющегося словарём, блокируется только целевой словарь. Итерируемый объект может быть параллельно изменён другим потоком:177178```python179d.update(iterable)        # iterable не является dict: заблокирован только d180d |= iterable             # iterable не является dict: заблокирован только d181dict.fromkeys(iterable)   # iterable не является dict/set/frozenset: заблокирован только результат182```183184Операции, включающие множественные обращения, а также итерацию, никогда не являются атомарными:185186```python187# НЕ атомарно: чтение-изменение-запись188d[key] = d[key] + 1189190# НЕ атомарно: проверка-затем-действие (TOCTOU)191if key in d:192    del d[key]193194# НЕ потокобезопасно: итерация во время изменения195for key, value in d.items():196    process(key)  # другой поток может изменить d197```198199Чтобы избежать проблем TOCTOU (проверка-в-один-момент, использование-в-другой), используйте атомарные операции или обрабатывайте исключения:200201```python202# Используйте pop() со значением по умолчанию вместо проверки с последующим удалением203d.pop(key, None)204205# Или обработайте исключение206try:207    del d[key]208except KeyError:209    pass210```211212Для безопасной итерации по словарю, который может быть изменён другим потоком, итерируйте по копии:213214```python215# Сделайте копию для безопасной итерации216for key, value in d.copy().items():217    process(key)218```219220Рассмотрите внешнюю синхронизацию при совместном использовании экземпляров [`dict`](https://python-all.ru/3/library/stdtypes.html#dict) между потоками.221222## Потокобезопасность объектов set223224Функция [`len()`](https://python-all.ru/3/library/functions.html#len) является безблокировочной и [атомарной](https://python-all.ru/3/glossary.html#term-atomic-operation).225226Следующая операция чтения является безблокировочной. Она не блокирует параллельные изменения и может наблюдать промежуточные состояния операций, которые удерживают блокировку объекта:227228```python229elem in s    # set.__contains__230```231232Эта операция может сравнивать элементы с помощью [`__eq__()`](https://python-all.ru/3/reference/datamodel.html#object.__eq__), который может выполнять произвольный код Python. Во время таких сравнений множество может быть изменено другим потоком. Для встроенных типов, таких как [`str`](https://python-all.ru/3/library/stdtypes.html#str), [`int`](https://python-all.ru/3/library/functions.html#int) и [`float`](https://python-all.ru/3/library/functions.html#float), `__eq__()` не освобождает базовую блокировку во время сравнений, и это не вызывает проблем.233234Все остальные операции, начиная с этого момента, удерживают блокировку объекта.235236Добавление или удаление одного элемента безопасно вызывать из нескольких потоков и не повредит множество:237238```python239s.add(elem)      # добавляет элемент240s.remove(elem)   # удаляет элемент, вызывает исключение, если отсутствует241s.discard(elem)  # удаляет элемент, если присутствует242s.pop()          # удаляет и возвращает произвольный элемент243```244245Эти операции также сравнивают элементы, поэтому применимы те же соображения относительно [`__eq__()`](https://python-all.ru/3/reference/datamodel.html#object.__eq__), что и выше.246247Метод [`copy()`](https://python-all.ru/3/library/stdtypes.html#set.copy) возвращает новый объект и удерживает блокировку объекта на всё время выполнения, так что он всегда атомарен.248249Метод [`clear()`](https://python-all.ru/3/library/stdtypes.html#set.clear) удерживает блокировку на всё время своего выполнения. Другие потоки не могут наблюдать удаление элементов.250251Следующие операции принимают только [`set`](https://python-all.ru/3/library/stdtypes.html#set) или [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset) в качестве операндов и всегда блокируют оба объекта:252253```python254s |= other                   # other должен быть set/frozenset255s &= other                   # other должен быть set/frozenset256s -= other                   # other должен быть set/frozenset257s ^= other                   # other должен быть set/frozenset258s & other                    # other должен быть set/frozenset259s | other                    # other должен быть set/frozenset260s - other                    # other должен быть set/frozenset261s ^ other                    # other должен быть set/frozenset262```263264[`set.update()`](https://python-all.ru/3/library/stdtypes.html#set.update), [`set.union()`](https://python-all.ru/3/library/stdtypes.html#set.union), [`set.intersection()`](https://python-all.ru/3/library/stdtypes.html#set.intersection) и [`set.difference()`](https://python-all.ru/3/library/stdtypes.html#set.difference) могут принимать несколько итерируемых объектов в качестве аргументов. Все они перебирают все переданные итерируемые объекты и выполняют следующее:265266> - **[`set.update()`](https://python-all.ru/3/library/stdtypes.html#set.update) и [`set.union()`](https://python-all.ru/3/library/stdtypes.html#set.union) блокируют оба объекта только когда**267>268>   другой операнд является [`set`](https://python-all.ru/3/library/stdtypes.html#set), [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset) или [`dict`](https://python-all.ru/3/library/stdtypes.html#dict).269> - **[`set.intersection()`](https://python-all.ru/3/library/stdtypes.html#set.intersection) и [`set.difference()`](https://python-all.ru/3/library/stdtypes.html#set.difference) всегда пытаются заблокировать**270>271>   все объекты.272273[`set.symmetric_difference()`](https://python-all.ru/3/library/stdtypes.html#set.symmetric_difference) пытается заблокировать оба объекта.274275У обновлённых вариантов перечисленных выше методов также есть некоторые различия между ними:276277> - **[`set.difference_update()`](https://python-all.ru/3/library/stdtypes.html#set.difference_update) и [`set.intersection_update()`](https://python-all.ru/3/library/stdtypes.html#set.intersection_update) пытаются**278>279>   блокировать все объекты по одному.280> - **[`set.symmetric_difference_update()`](https://python-all.ru/3/library/stdtypes.html#set.symmetric_difference_update) блокирует аргументы только если они являются**281>282>   типа [`set`](https://python-all.ru/3/library/stdtypes.html#set), [`frozenset`](https://python-all.ru/3/library/stdtypes.html#frozenset) или [`dict`](https://python-all.ru/3/library/stdtypes.html#dict).283284Следующие методы всегда пытаются заблокировать оба объекта:285286```python287s.isdisjoint(other)          # оба заблокированы288s.issubset(other)            # оба заблокированы289s.issuperset(other)          # оба заблокированы290```291292Операции, включающие множественные обращения, а также итерации, никогда не являются атомарными:293294```python295# НЕ атомарно: проверка-затем-действие296if elem in s:297      s.remove(elem)298299# НЕ потокобезопасно: итерация во время изменения300for elem in s:301      process(elem)  # другой поток может изменить s302```303304Рекомендуется использовать внешнюю синхронизацию при совместном использовании экземпляров [`set`](https://python-all.ru/3/library/stdtypes.html#set) между потоками. Подробнее см. [Python support for free threading](https://python-all.ru/3/howto/free-threading-python.html#freethreading-python-howto).305306## Потокобезопасность объектов bytearray307308> Функция [`len()`](https://python-all.ru/3/library/functions.html#len) не использует блокировки и является [атомарной](https://python-all.ru/3/glossary.html#term-atomic-operation).309>310> Конкатенация и сравнения используют буферный протокол, который предотвращает изменение размера, но не удерживает блокировку для каждого объекта. Эти операции могут наблюдать промежуточные состояния параллельных изменений:311>312> ```python313> ba + other    # может наблюдать конкурентные записи314> ba == other   # может наблюдать конкурентные записи315> ba < other    # может наблюдать конкурентные записи316> ```317>318> Все остальные операции, начиная с этого момента, удерживают блокировку на объект.319>320> Чтение одного элемента или среза безопасно вызывать из нескольких потоков:321>322> ```python323> ba[i]        # bytearray.__getitem__324> ba[i:j]      # срез325> ```326>327> Следующие операции безопасно вызывать из нескольких потоков, и они не повредят bytearray:328>329> ```python330> ba[i] = x         # запись одного байта331> ba[i:j] = values  # запись среза332> ba.append(x)      # добавление одного байта333> ba.extend(other)  # расширение итерируемым объектом334> ba.insert(i, x)   # вставка одного байта335> ba.pop()          # удаление и возврат последнего байта336> ba.pop(i)         # удаление и возврат байта по индексу337> ba.remove(x)      # удаление первого вхождения338> ba.reverse()      # обращение на месте339> ba.clear()        # удаление всех байтов340> ```341>342> Присваивание среза блокирует оба объекта, когда *значения* является [`bytearray`](https://python-all.ru/3/library/stdtypes.html#bytearray):343>344> ```python345> ba[i:j] = other_bytearray  # оба заблокированы346> ```347>348> Следующие операции возвращают новые объекты и удерживают блокировку на объект на всё время выполнения:349>350> ```python351> ba.copy()     # возвращает поверхностную копию352> ba * n        # повтор в новый bytearray353> ```354>355> Проверка принадлежности удерживает блокировку на всё время выполнения:356>357> ```python358> x in ba       # bytearray.__contains__359> ```360>361> Все остальные методы bytearray (такие как [`find()`](https://python-all.ru/3/library/stdtypes.html#bytearray.find), [`replace()`](https://python-all.ru/3/library/stdtypes.html#bytearray.replace), [`split()`](https://python-all.ru/3/library/stdtypes.html#bytearray.split), [`decode()`](https://python-all.ru/3/library/stdtypes.html#bytearray.decode) и т.д.) удерживают блокировку на объект на всё время своего выполнения.362>363> Операции, включающие множественные обращения, а также итерации, никогда не являются атомарными:364>365> ```python366> # НЕ атомарно: проверка-затем-действие367> if x in ba:368>     ba.remove(x)369>370> # НЕ потокобезопасно: итерация во время изменения371> for byte in ba:372>     process(byte)  # другой поток может изменить ba373> ```374>375> Для безопасной итерации по bytearray, который может быть изменён другим потоком, выполняйте итерацию по копии:376>377> ```python378> # Сделайте копию для безопасной итерации379> for byte in ba.copy():380>     process(byte)381> ```382>383> Рекомендуется использовать внешнюю синхронизацию при совместном использовании экземпляров [`bytearray`](https://python-all.ru/3/library/stdtypes.html#bytearray) между потоками. Подробнее см. [Python support for free threading](https://python-all.ru/3/howto/free-threading-python.html#freethreading-python-howto).384385## Потокобезопасность объектов memoryview386387Объекты [`memoryview`](https://python-all.ru/3/library/stdtypes.html#memoryview) предоставляют доступ к внутренним данным базового объекта без копирования. Потокобезопасность зависит как от самого memoryview, так и от экспортёра буфера базового объекта.388389Реализация memoryview использует атомарные операции для отслеживания собственных экспортов в [сборке со свободной многопоточностью](https://python-all.ru/3/glossary.html#term-free-threaded-build). Создание и освобождение memoryview потокобезопасны. Доступ к атрибутам (например, [`shape`](https://python-all.ru/3/library/stdtypes.html#memoryview.shape), [`format`](https://python-all.ru/3/library/stdtypes.html#memoryview.format)) читает поля, которые неизменны на время существования memoryview, поэтому одновременное чтение безопасно, пока memoryview не был освобождён.390391Однако фактические данные, доступ к которым осуществляется через memoryview, принадлежат базовому объекту. Одновременный доступ к этим данным безопасен только в том случае, если базовый объект это поддерживает:392393- Для неизменяемых объектов, таких как [`bytes`](https://python-all.ru/3/library/stdtypes.html#bytes), одновременное чтение через несколько memoryview безопасно.394- Для изменяемых объектов, таких как [`bytearray`](https://python-all.ru/3/library/stdtypes.html#bytearray), чтение и запись одной и той же области памяти из нескольких потоков без внешней синхронизации небезопасны и могут привести к повреждению данных. Обратите внимание, что даже memoryviews, доступные только для чтения, изменяемых объектов не предотвращают гонку данных, если базовый объект изменяется из другого потока.395396```python397# НЕ безопасно: конкурентные записи в один буфер398data = bytearray(1000)399view = memoryview(data)400# Поток 1: view[0:500] = b'x' * 500401# Поток 2: view[0:500] = b'y' * 500402```403404```python405# Безопасно: используется блокировка для конкурентного доступа406import threading407lock = threading.Lock()408data = bytearray(1000)409view = memoryview(data)410411with lock:412    view[0:500] = b'x' * 500413```414415Изменение размера или перераспределение базового объекта (например, вызов [`bytearray.resize()`](https://python-all.ru/3/library/stdtypes.html#bytearray.resize)) во время экспорта memoryview вызывает исключение [`BufferError`](https://python-all.ru/3/library/exceptions.html#BufferError). Это действует независимо от потоков.416