Telegram Group & Telegram Channel
🖥 Задача: "Кэширование и ленивые вычисления в многопоточном окружении"

🔜 Условие:

Вам необходимо создать декоратор @thread_safe_cached, который:

- Кэширует результат вызова функции по её аргументам (аналогично functools.lru_cache, но реализованный самостоятельно).
- Если несколько потоков одновременно вызывают функцию с одинаковыми аргументами:
- Только один поток фактически выполняет функцию,
- Остальные ждут, пока результат будет вычислен, и получают готовый результат.
- Кэш никогда не очищается (неограниченный размер).

Ограничения:

- Решение должно работать для любых функций и аргументов (должны быть хэшируемыми).
- Нельзя использовать готовый functools.lru_cache или другие библиотеки кэширования.
- Необходимо обеспечить корректную работу в многопоточной среде без гонок данных.

---

▪️ Подсказки:

- Для кэширования можно использовать словарь с ключами по аргументам (`*args`, `**kwargs`).
- Для защиты доступа к кэшу потребуется threading.Lock.
- Для ожидания завершения вычислений другими потоками можно применять threading.Event.
- Продумайте, как отличить "результат уже посчитан" от "результат в процессе вычисления".

---

▪️ Что оценивается:

- Умение работать с многопоточностью в Python.
- Правильная организация кэширования.
- Чистота и лаконичность кода.
- Умение обрабатывать тонкие случаи, например, одновременные вызовы.

---

▪️ Разбор возможного решения:

Основная идея:

- Создать кэш cache: Dict[Key, Result].
- Одновременно создать словарь "ожиданий" in_progress: Dict[Key, threading.Event].
- Если кто-то начал вычисление значения:
- Остальные ждут Event, пока он не будет установлен.

Пример реализации:

```python
import threading
import functools

def thread_safe_cached(func):
cache = {}
in_progress = {}
lock = threading.Lock()

@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
with lock:
if key in cache:
return cache[key]
if key not in in_progress:
in_progress[key] = threading.Event()
in_progress[key].clear()
creator = True
else:
creator = False

if creator:
try:
result = func(*args, **kwargs)
with lock:
cache[key] = result
finally:
in_progress[key].set()
with lock:
del in_progress[key]
return result
else:
in_progress[key].wait()
with lock:
return cache[key]

return wrapper
```

---

▪️ Пояснения к коду:

- При первом вызове для новых аргументов поток создаёт Event и начинает вычислять результат.
- Остальные потоки видят Event и вызывают wait(), пока первый поток не установит set().
- Как только результат вычислен, Event сигнализирует всем ждущим потокам, что данные готовы.
- Доступ к cache и in_progress защищён через lock для предотвращения гонок.

---

▪️ Возможные подводные камни:

- Если не удалять Event из in_progress, кэш постепенно заполнится мусором.
- Если произойдёт ошибка внутри func, необходимо всё равно освободить Event, иначе потоки будут бесконечно ждать.
- Нельзя удерживать lock во время выполнения тяжёлой функции func, иначе все потоки будут блокироваться.

---

▪️ Вопросы на собеседовании по этой задаче:

- Как изменить реализацию, чтобы кэш имел ограничение по размеру (например, максимум 1000 элементов)?
- Как адаптировать декоратор под асинхронные функции (`async def`)?
- Что произойдет, если func иногда вызывает исключения? Как кэшировать ошибки или не кэшировать их?
- Как изменить реализацию так, чтобы кэш удалял устаревшие данные через TTL (Time-To-Live)?

@Python_Community_ru



tg-me.com/Python_Community_ru/2583
Create:
Last Update:

🖥 Задача: "Кэширование и ленивые вычисления в многопоточном окружении"

🔜 Условие:

Вам необходимо создать декоратор @thread_safe_cached, который:

- Кэширует результат вызова функции по её аргументам (аналогично functools.lru_cache, но реализованный самостоятельно).
- Если несколько потоков одновременно вызывают функцию с одинаковыми аргументами:
- Только один поток фактически выполняет функцию,
- Остальные ждут, пока результат будет вычислен, и получают готовый результат.
- Кэш никогда не очищается (неограниченный размер).

Ограничения:

- Решение должно работать для любых функций и аргументов (должны быть хэшируемыми).
- Нельзя использовать готовый functools.lru_cache или другие библиотеки кэширования.
- Необходимо обеспечить корректную работу в многопоточной среде без гонок данных.

---

▪️ Подсказки:

- Для кэширования можно использовать словарь с ключами по аргументам (`*args`, `**kwargs`).
- Для защиты доступа к кэшу потребуется threading.Lock.
- Для ожидания завершения вычислений другими потоками можно применять threading.Event.
- Продумайте, как отличить "результат уже посчитан" от "результат в процессе вычисления".

---

▪️ Что оценивается:

- Умение работать с многопоточностью в Python.
- Правильная организация кэширования.
- Чистота и лаконичность кода.
- Умение обрабатывать тонкие случаи, например, одновременные вызовы.

---

▪️ Разбор возможного решения:

Основная идея:

- Создать кэш cache: Dict[Key, Result].
- Одновременно создать словарь "ожиданий" in_progress: Dict[Key, threading.Event].
- Если кто-то начал вычисление значения:
- Остальные ждут Event, пока он не будет установлен.

Пример реализации:

```python
import threading
import functools

def thread_safe_cached(func):
cache = {}
in_progress = {}
lock = threading.Lock()

@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
with lock:
if key in cache:
return cache[key]
if key not in in_progress:
in_progress[key] = threading.Event()
in_progress[key].clear()
creator = True
else:
creator = False

if creator:
try:
result = func(*args, **kwargs)
with lock:
cache[key] = result
finally:
in_progress[key].set()
with lock:
del in_progress[key]
return result
else:
in_progress[key].wait()
with lock:
return cache[key]

return wrapper
```

---

▪️ Пояснения к коду:

- При первом вызове для новых аргументов поток создаёт Event и начинает вычислять результат.
- Остальные потоки видят Event и вызывают wait(), пока первый поток не установит set().
- Как только результат вычислен, Event сигнализирует всем ждущим потокам, что данные готовы.
- Доступ к cache и in_progress защищён через lock для предотвращения гонок.

---

▪️ Возможные подводные камни:

- Если не удалять Event из in_progress, кэш постепенно заполнится мусором.
- Если произойдёт ошибка внутри func, необходимо всё равно освободить Event, иначе потоки будут бесконечно ждать.
- Нельзя удерживать lock во время выполнения тяжёлой функции func, иначе все потоки будут блокироваться.

---

▪️ Вопросы на собеседовании по этой задаче:

- Как изменить реализацию, чтобы кэш имел ограничение по размеру (например, максимум 1000 элементов)?
- Как адаптировать декоратор под асинхронные функции (`async def`)?
- Что произойдет, если func иногда вызывает исключения? Как кэшировать ошибки или не кэшировать их?
- Как изменить реализацию так, чтобы кэш удалял устаревшие данные через TTL (Time-To-Live)?

@Python_Community_ru

BY Python Community


Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283

Share with your friend now:
tg-me.com/Python_Community_ru/2583

View MORE
Open in Telegram


Python Community Telegram | DID YOU KNOW?

Date: |

How Does Bitcoin Work?

Bitcoin is built on a distributed digital record called a blockchain. As the name implies, blockchain is a linked body of data, made up of units called blocks that contain information about each and every transaction, including date and time, total value, buyer and seller, and a unique identifying code for each exchange. Entries are strung together in chronological order, creating a digital chain of blocks. “Once a block is added to the blockchain, it becomes accessible to anyone who wishes to view it, acting as a public ledger of cryptocurrency transactions,” says Stacey Harris, consultant for Pelicoin, a network of cryptocurrency ATMs. Blockchain is decentralized, which means it’s not controlled by any one organization. “It’s like a Google Doc that anyone can work on,” says Buchi Okoro, CEO and co-founder of African cryptocurrency exchange Quidax. “Nobody owns it, but anyone who has a link can contribute to it. And as different people update it, your copy also gets updated.”

How to Buy Bitcoin?

Most people buy Bitcoin via exchanges, such as Coinbase. Exchanges allow you to buy, sell and hold cryptocurrency, and setting up an account is similar to opening a brokerage account—you’ll need to verify your identity and provide some kind of funding source, such as a bank account or debit card. Major exchanges include Coinbase, Kraken, and Gemini. You can also buy Bitcoin at a broker like Robinhood. Regardless of where you buy your Bitcoin, you’ll need a digital wallet in which to store it. This might be what’s called a hot wallet or a cold wallet. A hot wallet (also called an online wallet) is stored by an exchange or a provider in the cloud. Providers of online wallets include Exodus, Electrum and Mycelium. A cold wallet (or mobile wallet) is an offline device used to store Bitcoin and is not connected to the Internet. Some mobile wallet options include Trezor and Ledger.

Python Community from us


Telegram Python Community
FROM USA