← Назад к вопросам

Приведи пример атомарной операции

2.0 Middle🔥 121 комментариев
#Python Core#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Атомарные операции в 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())  # Атомарная операция

Ключевые выводы

  1. В CPython простые операции (присваивание, append, dict[key]) атомарны благодаря GIL
  2. Для сложных операций используй Lock или asyncio.Lock
  3. Queue — встроенный thread-safe контейнер
  4. На уровне БД используй атомарные SQL операции
  5. В распределенных системах нужна внешняя синхронизация

Атомарность — основа надежности многопоточных и асинхронных систем.

Приведи пример атомарной операции | PrepBro