Приведи пример атомарной операции
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Атомарные операции в Python
Атомарная операция — это действие, которое выполняется полностью и неделимо. Она либо полностью завершится, либо не начнется вообще. Если несколько потоков обращаются к одним данным одновременно, атомарная операция не может быть нарушена на полпути.
Основной концепт
Без атомарности может быть race condition:
# Плохо — НЕ атомарное
balance = 100
# Операция: balance = balance + 50
# В многопоточном окружении может произойти:
# Поток 1: читает balance (100)
# Поток 2: читает balance (100) <- Race condition!
# Поток 1: пишет 100 + 50 = 150
# Поток 2: пишет 100 + 50 = 150 <- Потеряны 50 рублей!
Примеры атомарных операций в Python
1. Простые операции (гарантированно атомарные в CPython из-за GIL)
import threading
# Атомарные операции (на CPython)
x = 0
# Присваивание числа — атомарно
x = 42 # Один bytecode
# Присваивание строки — атомарно
name = "John" # Один bytecode
# Append к списку — атомарно
items = []
items.append("value") # Один bytecode
# Dict присваивание ключа — атомарно
data = {}
data["key"] = "value" # Один bytecode
Почему это атомарные операции? В CPython есть Global Interpreter Lock (GIL), который гарантирует, что только один поток может выполнять Python bytecode одновременно. Поэтому простые операции, которые выполняются за один bytecode, являются атомарными.
2. Атомарные операции с Lock
Для сложных операций нужна явная синхронизация:
import threading
class BankAccount:
def __init__(self, balance: int):
self.balance = balance
self.lock = threading.Lock()
# НЕ атомарное
def transfer_unsafe(self, amount: int):
if self.balance >= amount:
self.balance -= amount # Race condition!
return True
return False
# Атомарное
def transfer_safe(self, amount: int) -> bool:
with self.lock: # Атомарная секция
if self.balance >= amount:
self.balance -= amount
return True
return False
account = BankAccount(1000)
def worker():
for _ in range(1000):
account.transfer_safe(10)
threads = [threading.Thread(target=worker) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(account.balance) # Всегда будет 0
3. Атомарные операции с asyncio
В асинхронной программе атомарность работает иначе. Коротины переключаются в точках await, поэтому атомарная операция — это код между await.
import asyncio
class AsyncCounter:
def __init__(self):
self.count = 0
self.lock = asyncio.Lock()
# НЕ атомарное
async def increment_unsafe(self):
current = self.count
await asyncio.sleep(0) # Context switch!
self.count = current + 1 # Race condition!
# Атомарное
async def increment_safe(self):
async with self.lock: # Атомарная секция
self.count += 1 # Другие корутины ждут
async def test():
counter = AsyncCounter()
tasks = [counter.increment_safe() for _ in range(1000)]
await asyncio.gather(*tasks)
print(counter.count) # Всегда 1000
asyncio.run(test())
4. Queue.put() и Queue.get() — встроенные атомарные операции
import queue
import threading
# Очередь thread-safe по умолчанию
q = queue.Queue()
# Атомарные операции
def producer():
for i in range(1000):
q.put(i) # Атомарно добавляет элемент
def consumer():
while True:
value = q.get() # Атомарно забирает элемент
if value is None:
break
print(f"Consumed: {value}")
5. Compare-And-Swap (CAS) операция
from threading import Thread, Lock
class AtomicCounter:
def __init__(self):
self._value = 0
self._lock = Lock()
def compare_and_set(self, expected: int, new_value: int) -> bool:
"""CAS: если value == expected, меняет на new_value."""
with self._lock:
if self._value == expected:
self._value = new_value
return True
return False
def get(self) -> int:
with self._lock:
return self._value
counter = AtomicCounter()
print(counter.compare_and_set(0, 42)) # True
print(counter.get()) # 42
Практические примеры
Безопасное увеличение счетчика в БД
from sqlalchemy import update
from sqlalchemy.orm import Session
def increment_view_count(post_id: int, db: Session):
# Атомарная на уровне БД
db.execute(
update(Post)
.where(Post.id == post_id)
.values(view_count=Post.view_count + 1)
)
db.commit()
Безопасная передача между потоками
import threading
from queue import Queue
results = Queue() # Thread-safe контейнер
def worker(task_id):
result = do_work(task_id)
results.put(result) # Атомарная операция
threads = [threading.Thread(target=worker, args=(i,)) for i in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
while not results.empty():
print(results.get()) # Атомарная операция
Ключевые выводы
- В CPython простые операции (присваивание, append, dict[key]) атомарны благодаря GIL
- Для сложных операций используй Lock или asyncio.Lock
- Queue — встроенный thread-safe контейнер
- На уровне БД используй атомарные SQL операции
- В распределенных системах нужна внешняя синхронизация
Атомарность — основа надежности многопоточных и асинхронных систем.