Теадобезопасный объект и типы dict в python - PullRequest
1 голос
/ 28 октября 2011

У меня есть два вопроса о создании потоково-безопасных типов в python и один связанный вопрос о множественном наследовании.

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

2) Другой вопрос, который у меня возникает, заключается в том, существуют ли более разумные альтернативы этим типам в pythonв типичной установке.

Пример:

from threading import Lock
from __future__ import with_statement

class safedict(dict):
    def __init__(self,*args,**kwargs):
        self.mylock=Lock(); 
        super(safedict, self).__init__(*args, **kwargs)
    def __setitem__(self,*args,**kwargs):
        with self.mylock:
            print " DEBUG: Overloaded __setitem__ has the lock now."
            super(safedict,self).__setitem__(*args,**kwargs)


class safeobject(object):
    mylock = Lock(); # a temporary useless lock, until we have a proper instance.
    def __init__(self,*args,**kwargs):
        self.mylock=Lock(); 
        super(safeobject, self).__init__(*args, **kwargs)
    def __setattr__(self,*args,**kwargs):
        with self.mylock:
            print " DEBUG: Overloaded __setattr__ has the lock now."
            super(safeobject,self).__setattr__(*args,**kwargs)

3) Если оба типа, определенных выше, можно считать достаточно безопасными, с какими отрицательными последствиями столкнется использование множественного наследования для созданиятип, который поддерживает смесь обеих этих модификаций, и наследует ли мой пример эти классы в оптимальном порядке?

Пример:

class safedict2(safeobject,dict):
    def __setitem__(self,*args,**kwargs):
        with self.mylock:
            print " DEBUG: Overloaded __setitem__ has the lock now."
            super(safedict2,self).__setitem__(*args,**kwargs)

Редактировать: просто еще один пример другого типа, наследующего обаиз предыдущих типов, и тестирование с использованием ipython.

In [304]: class safedict3(safeobject,safedict):
   .....:         pass
   .....: 

In [305]: d3 = safedict3()
 DEBUG: Overloaded __setattr__ has the lock now.
 DEBUG: Overloaded __setattr__ has the lock now.

In [306]: d3.a=1
 DEBUG: Overloaded __setattr__ has the lock now.

In [307]: d3['b']=2
 DEBUG: Overloaded __setitem__ has the lock now.

In [308]: d3
Out[308]: {'b': 2}

1 Ответ

1 голос
/ 28 октября 2011

Что касается вашего первого и второго вопросов, типы dict, list и т. Д. Уже поточно-ориентированы. Вам не нужно добавлять безопасность потоков к ним. Однако вы можете найти это полезным. Это декоратор, который в основном реализует ключевое слово synchronized из Java, используя область действия для определения критического раздела. Используя подобный подход, можно также создать threading.Condition ориентированный декоратор.

import threading

def tryfinally(finallyf):
  u"returns a decorator that adds try/finally behavior with given no-argument call in the finally"
  def decorator(callable):
    def execute(*args, **kwargs):
      try: result = callable(*args, **kwargs)
      finally: finallyf()
      return result
    return execute
  return decorator

def usinglock(lock):
  u"returns a decorator whose argument will acquire the given lock while executing"
  def decorator(function):
    body = tryfinally(lock.release)(function)
    def execute(*args, **kwargs):
      lock.acquire()
      return body(*args, **kwargs)
    return execute
  return decorator

def synchronized(function):
  u"decorator; only one thread can enter the decorated function at a time; recursion is OK"
  return usinglock(threading.RLock())(function)

Используйте это так (и остерегайтесь тупиков, если вы злоупотребляете им):

@synchronized
def foo(*args):
  print 'Only one thread can enter this function at a time'

По третьему вопросу в учебнике Python говорится, что порядок поиска унаследованных атрибутов - сначала глубина, слева слева. Так что если вы наследуете (myclass, dict), то следует использовать метод __setitem__ из myclass. (В более старых версиях Python этот же раздел учебника подразумевал, что этот выбор был произвольным, но в настоящее время он выглядит вполне осознанным.)

Я предполагаю, что по фрейдовскому описанию точки с запятой в опубликованном источнике вы новичок в Python, но имеете опыт работы с Java или C #. Если это так, вам нужно помнить, что разрешение атрибута (метода) происходит во время выполнения в Python и что классы , а также экземпляр являются первоклассными объектами, которые можно проверять / исследовать при запуске время.

Сначала ищется словарь атрибутов instance , затем атрибуты class , а затем запускается алгоритм поиска родительского класса. Это делается с (концептуально) эквивалентом повторных вызовов hasattr(class_or_instance, attribute).

Ниже подтверждается, что для классов «нового стиля» (классов, которые наследуются от object, что в спецификации языка 2.x является необязательным), это разрешение происходит каждый раз, когда выполняется поиск атрибута. Это не делается при создании класса (или подкласса) или при создании экземпляров. (Это было сделано в выпуске 2.7.2.)

>>> class Foo(object):
...   def baz(self):
...     print 'Original Foo.baz'
...
>>> class Bar(Foo): pass
...
>>> def newprint(self):
...   print 'New Foo.baz'
...
>>> x = Foo()
>>> y = Bar()
>>> Foo.baz = newprint
>>> a = Foo()
>>> b = Bar()
>>> map(lambda k: k.baz(), (x, y, a, b))
New Foo.baz
New Foo.baz
New Foo.baz
New Foo.baz
[None, None, None, None]

Замена метода класса Foo изменяет поведение уже определенных подклассов и уже созданных экземпляров.

...