Карта исполнителя не запускает функцию - PullRequest
1 голос
/ 04 апреля 2020

Я пытаюсь воспроизвести состояние гонки. Приведенный ниже код прекрасно работает с submit, который используется вместо map, но я хочу понять, почему map не запускает метод обновления.

import concurrent.futures
import time


class BankAccount:
    def __init__(self):
        self.balance = 100  # shared data

    def update(self, transaction, amount):
        print(f'{transaction} started')
        tmp_amount = self.balance  # reading from db
        tmp_amount += amount
        time.sleep(1)
        self.balance = tmp_amount
        print(f'{transaction} ended')


if __name__ == '__main__':
    acc = BankAccount()
    print(f'Main started. acc.Balance={acc.balance}')

    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as ex:
        ex.map(acc.update, [dict(transaction='refill', amount=100),
                            dict(transaction='withdraw', amount=-200)])

    print(f'End of Main. Balance={acc.balance}')

1 Ответ

2 голосов
/ 04 апреля 2020

Передайте каждый тип аргумента в отдельной итерируемой.

ex.map(acc.update, ['refill', 'withdraw'], [100, -200])

Как и map, Executor.map принимает одну итерацию на аргумент и присваивает от каждого повторяемого до одного аргумента. Кроме того, ошибки не распространяются до фактического доступа к результату.

Executor.map (забавно c, * iterables, timeout = None, chunksize = 1)

Аналогично map(func, *iterables) ...

Если вызов fun c вызывает исключение, то это исключение будет вызвано, когда его значение получено из итератора .

Таким образом, код неправильно передает аргументы, но подавляет ошибку. Выборка значений, например, через list(ex.map(...)) выявляет ошибку:

TypeError: update() missing 1 required positional argument: 'amount'

Создание отдельной итерации для каждого типа аргументов предотвращает это.

#                  V transaction
ex.map(acc.update, ['refill', 'withdraw'], [100, -200])
#                                          ^ amount

Может быть желательно упорядочить аргументы по вызову, а не по виду. Используйте zip и * для распаковки, чтобы преобразовать входные данные в соответствии с map.

ex.map(acc.update, *zip(('refill', 100), ('withdraw', 200)))

Если требуются аргументы ключевого слова, требуется помощник для распаковки аргументов.

def kwargify(func):
    """Wrap ``func`` to accept a dictionary of keyword arguments"""
    return lambda kwargs: func(**kwargs)

Просто оберните желаемую функцию этим помощником при передаче ее в ex.map:

    ex.map(
        kwargify(acc.update),
        [
            dict(transaction='refill', amount=100),
            dict(transaction='withdraw', amount=-200)
        ]
    )
...