Python中的线程安全对象和字典类型
我有两个关于在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 个回答
关于你提的第一个和第二个问题,像 dict
、list
这些类型本身就是线程安全的。你不需要再给它们添加线程安全的功能。不过,你可能会觉得下面这个东西有用。它是一个装饰器,基本上实现了 Java 中的 synchronized
关键字,利用函数的作用域来定义一个关键区域。用类似的方法,你也可以写一个基于 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)
,那么会使用 myclass
的 __setitem__
方法。(在旧版本的 Python 中,这部分内容暗示这个选择是任意的,但现在看来这个选择是很有意图的。)
我猜从你发的代码中出现的分号来看,你可能是刚接触 Python,但在 Java 或 C# 上有经验。如果是这样,你需要记住,Python 中属性(方法)的解析是在运行时进行的,而且 类 和实例都是可以在运行时被检查和探索的一等对象。
首先会查找 实例 的属性字典,然后查找 类 的属性,最后才是父类的查找算法。这一过程可以想象成反复调用 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
的方法会改变已经定义的子类和已经创建的实例的行为。