Threading.Lock на Python: атомарное изменение общего словаря
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Multiprocessing и threading Python: задачи на Queue, Lock и остановку.
Атомарное списание из кассеты
В расчётном модуле банкомата сразу несколько рабочих потоков параллельно обрабатывают списания купюр разных номиналов. Управление остатками ведётся в общем словаре баланса кассет, где для каждой из них фиксируется текущее количество банкнот и ведётся список аудита выполненных операций.
Каждое списание состоит из нескольких шагов: сначала нужно проверить доступный остаток, затем уменьшить его на требуемую величину и занести идентификатор операции в аудиторский список. Если выполнять эти действия без взаимного исключения, конкурирующие потоки могут допустить овердрафт кассеты или перезаписать чужие транзакции. Чтобы сохранить целостность данных, обновление баланса защищают с помощью примитива threading.Lock.
Реализуйте логику потока-оператора, которая под защитой Lock атомарно проверяет наличие нужного количества купюр, списывает их и регистрирует операцию в журнале кассеты, а при нехватке средств возвращает отказ без изменения реестра.
Что уже дано
При выполнении кода доступны структура CashRegister, а также вспомогательные функции make_cash_register(initial, shared_lock=False) и observe_cash_debits(initial, operations, shared_lock=False) для локальной проверки списаний. Экземпляр CashRegister содержит:
balances— отображение идентификаторов кассет на их целочисленные балансы банкнот;audit— отображение идентификаторов кассет на списки строк с идентификаторами успешных операций;locks— отображение идентификатора кассеты на соответствующий объект блокировки со стандартным интерфейсомthreading.Lock.
Вам предстоит реализовать функцию try_cash_debit.
Что нужно сделать
Напишите функцию try_cash_debit(register, cassette, quantity, operation_id) -> bool.
Параметры функции:
register— разделяемый реестрCashRegister, содержащий от 0 до 8 кассет;cassette— непустая строка ASCII длиной до 30 символов, гарантированно присутствующая среди ключей реестра;quantity— целое число запрашиваемых купюр от 1 до 100 000;operation_id— уникальный непустой строковый идентификатор операции длиной до 64 символов.
Правила списания:
- Все действия по проверке остатка, изменению баланса и регистрации операции должны выполняться строго под защитой блокировки, закреплённой за данной кассетой в
register.locks[cassette]. - Если текущий остаток в
register.balances[cassette]больше или равенquantity, операция считается успешной: остаток уменьшается наquantity, строкаoperation_idдобавляется методомappend()в список аудитаregister.audit[cassette], а функция возвращаетTrue. - Если доступных купюр меньше
quantity, баланс кассеты и список её аудита остаются без изменений, а функция возвращаетFalse. - Функция работает непосредственно с переданным общим объектом
register. Не допускается возникновение отрицательного баланса (овердрафта) при одновременной работе до 16 конкурентных потоков.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня try_cash_debit и вызывает её с проверочными аргументами.
Пример использования
register = make_cash_register({'R50': 3})
print(try_cash_debit(register, 'R50', 2, 'op1'))
print(try_cash_debit(register, 'R50', 2, 'op2'))
print(dict(register.balances))
print({key: list(items) for key, items in register.audit.items()})
Вывод:
True
False
{'R50': 1}
{'R50': ['op1']}
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Multiprocessing.Queue и join(): причина взаимной блокировки Python
- Остановка пула процессов Python: Queue, маркеры завершения и join
- Multiprocessing Python: ограничение очереди семафором
- Процесс и потоки Python: двухфазная обработка матрицы
- Queue и Lock в Python: дедупликация заявок между рабочими потоками
- Удаление ключа из ChainMap в Python: проверка поведения del
