Как безопасно изменить свойства класса в отдельных потоках и вернуть их из них? - PullRequest
0 голосов
/ 03 апреля 2019

Я определил класс Test, один из методов которого updater запускается в отдельном потоке и постоянно обновляет свойство steps. Я надеюсь на некоторые окончательные советы относительно безопасной модификации собственности и безопасного доступа к собственности в других темах.

Ниже приведен некоторый рабочий код, который отвечает моим требованиям, и широко использует блокировку потока, как предложено здесь . Я развернул блокировку в обоих потоках, а также получил и установил методы, поскольку все операции над свойством steps не являются атомарными (или, скорее всего, будут в моем законченном коде).

from threading import Thread, Lock
from time import sleep


class Test:
    def __init__(self):
        self.lock = Lock()
        self.steps = 5
        self.thread = Thread(target=self.updater)
        self.threadRun = True
        self.thread.start()

    def updater(self):
        while self.threadRun:
            with self.lock:
                self.steps += 1
            sleep(0.001)
        print('Thread stopped')

    def get_steps(self):
        with self.lock:
            return self.steps

    def set_steps(self, steps):
        with self.lock:
            self.steps = steps

Я понимаю, что непосредственный доступ к свойствам экземпляра предпочтительнее использования методов getter и setter:

a = Test()
steps = a.steps
a.steps = 5

вместо:

a = Test()
steps = a.get_steps()
a.set_steps(5)

но я использовал их здесь, чтобы включить замок. Я не уверен в атомарности прямого доступа к собственности в этом случае, поэтому мог бы избежать блокировки, но кто-то может подтвердить, если это так? Если я все еще должен использовать блокировку и использовать методы getter и setter для каждого свойства, к которому я хочу получить доступ, как мне развернуть декоратор в классе, чтобы сделать его более умным, когда я масштабируюсь до нескольких свойства? Я ожидаю что-то вроде этого:

from threading import Thread, Lock
from time import sleep


class Test:
    def __init__(self):
        self.lock = Lock()
        self.steps = 5
        self.thread = Thread(target=self.updater)
        self.threadRun = True
        self.thread.start()

    def updater(self):
        while self.threadRun:
            with self.lock:
                self.steps += 1
            sleep(0.001)
        print('Thread stopped')

    def locker(self, func):
        with self.lock:
            return func

    @locker
    def get_steps(self):
        return self.steps

    @locker
    def set_steps(self, steps):
        self.steps = steps

Но в этот момент мои навыки обработки аргументов между классами и декораторами заканчиваются, и я получаю:

TypeError: locker() missing 1 required positional argument: 'func'

Некоторое понимание здесь , но согласно первому комментарию к решению, мне нужно правильно разобраться с аргументами. Я полагаю, что мог бы поместить функцию locker вне класса, но я надеялся поместить ее в класс для аккуратности - я бы не использовал ее в другом месте. Какое здесь элегантное решение?

1 Ответ

0 голосов
/ 11 апреля 2019

Хорошо, после некоторой помощи от этого вопроса у меня есть работоспособное решение:

from threading import Thread, Lock
import time


class Blah():
    def __init__(self):
        self.lock = Lock()
        self.a = 0
        self.thread = Thread(target=self.updater)
        self.threadRun = True
        self.thread.start()

    def locker(func):
        def magic(self, *args):
            with self.lock:
                print('locked')
                return func(self, *args)
        return magic

    def updater(self):
        t_start = time.time()
        while self.threadRun and time.time() - t_start <= 20:
            self.a += 1
            time.sleep(2)
        print('Thread stopped')

    @property
    @locker
    def a(self):
        return self._a

    @a.setter
    @locker
    def a(self, val):
        self._a = val

Сейчас я пытаюсь применить этот подход ко многим атрибутам экземпляра, а не толькоa и я ищу питонический / динамический способ сделать это, кроме двух дискретных блоков кода (@property и @attr.setter) для каждого.В идеале я бы инициализировал каждый атрибут в конструкторе __init__, который одновременно назначал бы начальное значение и настраивал методы @property и @attr.setter, соответствующим образом заключенные в @locker.Кто-нибудь может подсказать, как это сделать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...