Потокобезопасное свойство / атрибут Python? - PullRequest
3 голосов
/ 05 августа 2011

У меня есть код, подобный следующему:

class SomeSharedData(object):
    def __init__(self):
        self._lock = RLock()
        self._errors = 0

    @property
    def errors(self):
        with self._lock:
        return self._errors

    @errors.setter
    def errors(self, value):
        with self._lock:
            self._errors = value

За исключением других свойств, таких как errors.Моя цель здесь - простота использования, а не эффективность, поэтому избыточная блокировка - это нормально.

Есть ли более краткий способ определения этих свойств?

Лучшее, что я придумала до сих пор, это:

class thread_safe_property(object):
    def __init__(self, name=None):
        self.name = name

    def __get__(self, obj, objtype):
        with obj._lock:
            return getattr(obj, self.name)

    def __set__(self, obj, val):
        with obj._lock:
            setattr(obj, self.name, val)

class SomeSharedData(object):
    def __init__(self):
        self._lock = RLock()
        self._errors = 0

    errors = thread_safe_property('_errors')

Идеи?Лучшие пути?

Как исходный код, так и новый подход страдают от возможных условий гонки для операторов типа data.errors += 1, но мне редко нужно выполнять эти операции, поэтому я добавляю обходные пути там, где это необходимо.

Спасибо!

1 Ответ

10 голосов
/ 05 августа 2011

Возможно, вам нужно немного подумать о том, что значит быть потокобезопасным.Подумайте, написали ли вы этот код вместо этого:

class SomeSharedData(object):
    def __init__(self):
        self.errors = 0

Этот код точно такой же потокобезопасный, как и код, который вы опубликовали.Присвоение значения атрибуту является потокобезопасным в Python: значение всегда присваивается;оно может или не может быть перезаписано другим значением, назначенным из другого потока, но вы всегда получаете одно или другое значение, никогда не смешивая их.Аналогичным образом, доступ к атрибуту дает вам текущее значение.

Если ваш код разрывается, то, как вы сказали в исходной или упрощенной версии, строка, такая как:

shared.errors += 1

, неПотокобезопасен, но в этом весь смысл обеспечения безопасности вашего кода, это то, на что нужно обращать внимание, а не просто get / set.

Ответ на вопрос в комментарии:

Простое назначение в Python - это просто повторное связывание имени (не создание копии) и гарантированное атомарное;Вы получаете одно значение или другое.Однако присвоение атрибуту (или переменной с подпиской) может быть переопределено, как в вашем свойстве выше.В этом случае присвоение атрибута может потенциально нарушиться.Таким образом, ответ заключается в том, что присвоение атрибута обычно безопасно, но не в том случае, если оно было переопределено свойством или setattr или аналогичным.

Кроме того, если заменяется старое значениекласс Python с деструктором, или что-то, что содержит класс Python с деструктором, тогда код деструктора может выполняться в середине присваивания.Python по-прежнему будет гарантировать, что его собственные структуры данных не будут повреждены (поэтому вы никогда не должны получить segfault), но он не будет делать то же самое для ваших собственных.Очевидным решением для этого никогда не является определение del для любого из ваших классов.

...