我有以下情况:
我想出了以下模式:
#!/usr/bin/env python
import threading
import time
class MyObj1:
def __init__(self, name):
self.name = name
local = threading.local()
def get_local_obj(key, create_obj, *pars, **kwargs):
d = local.__dict__
if key in d: obj = d[key]
else :
obj = create_obj(*pars, **kwargs)
d[key] = obj
return obj
class Worker(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
myobj1 = get_local_obj('obj1', MyObj1, (self.name))
for _ in xrange(3):
print myobj1.name
time.sleep(1)
def test():
ths = [Worker() for _ in xrange(2)]
for t in ths : t.start()
test()
这里我自己创建线程,因为这只是一个测试,但正如所说,在实际应用程序中,我无法控制线程。
我感兴趣的是函数get_local_obj
。我有几个问题:
只是为了澄清:我的应用程序是多线程的,但创建线程的不是我。我只是创建一些对象,这些对象恰好在框架创建的线程内运行。我的一些对象不是线程安全的,所以我需要每个线程只创建一次。因此get_my_object
。
local = threading.local()。
答案 0 :(得分:1)
这个怎么样?
class Worker (Thread):
def __init__(self):
super(Worker,self).__init__()
self.m_local = threading.local()
def get_my_obj(self):
try:
obj = self.m_local.my_object
except AttributeError:
self.m_local.my_object = create_object()
obj = self.m_local.my_object
return obj
def run(self):
my_obj = self.get_my_obj()
# ...
最后它与你的例子相似,只是更干净。你将所有特定于线程的代码保存在一个地方,run
函数“不知道”关于初始化的任何内容,它使用getter获取my_obj
,而getter只创建一次对象。 threading.local将保证数据是特定于线程的 - 这是它的工作。
我没有看到任何内存泄漏的原因。最后,你需要出汗才能在python中获得泄漏:)
答案 1 :(得分:1)
FWIW,这是您的代码的修改版本,根据answer和another相关问题进行了简化。但它仍然基本上是相同的模式。
#!/usr/bin/env python
import threading
import time
threadlocal = threading.local()
class MyObj1(object):
def __init__(self, name):
print 'in MyObj1.__init__(), name =', name
self.name = name
def get_local_obj(varname, factory, *args, **kwargs):
try:
return getattr(threadlocal, varname)
except AttributeError:
obj = factory(*args, **kwargs)
setattr(threadlocal, varname, obj)
return obj
class Worker(threading.Thread):
def __init__(self):
super(Worker, self).__init__()
def run(self):
myobj1 = get_local_obj('obj1', MyObj1, self.name)
for _ in xrange(3):
print myobj1.name
time.sleep(1)
def test():
ths = [Worker() for _ in xrange(3)]
for t in ths:
t.start()
test()
实际上,如果没有get_local_obj()
:
#!/usr/bin/env python
import threading
import time
threadlocal = threading.local()
class MyObj1(object):
def __init__(self, name):
print 'in MyObj1.__init__(), name =', name
self.name = name
class Worker(threading.Thread):
def __init__(self):
super(Worker, self).__init__()
def run(self):
threadlocal.myobj1 = MyObj1(self.name)
for _ in xrange(3):
print threadlocal.myobj1.name
time.sleep(1)
def test():
ths = [Worker() for _ in xrange(3)]
for t in ths:
t.start()
test()
答案 2 :(得分:0)
这是另一个不同的答案,它利用了我拥有线程级单例的想法。它完全摆脱了你的get_local_obj()
功能。我没有做过很多测试,但到目前为止它似乎都有效。它可能比你想要的更多,因为它实际上实现了你在上一个要点中所说的你想要的东西:
可以在多个位置实例化对象,但是如果我在已定义它的一个线程中重新实例化同一个对象,则应该重用该对象。
#!/usr/bin/env python
import threading
import time
threadlocal = threading.local()
class ThreadSingleton(type):
# called when instances of client classes are created
def __call__(cls, *args, **kwargs):
instances = threadlocal.__dict__.setdefault(cls.__name__+'.instances', {})
if cls not in instances:
instances[cls] = super(ThreadSingleton, cls).__call__(*args, **kwargs)
return instances[cls]
class MyClass(object):
__metaclass__ = ThreadSingleton
def __init__(self, name):
print 'in MyClass.__init__(), name =', name
self.name = name
class Worker(threading.Thread):
def __init__(self):
super(Worker, self).__init__()
def run(self):
myobj1 = MyClass(self.name)
for _ in xrange(3):
print 'myobj1.name:', myobj1.name
myobj2 = MyClass(self.name+'#2') # this returns myobj1
print 'myobj2.name:', myobj2.name # so this prints myobj1.name
time.sleep(1)
def test():
ths = [Worker() for _ in xrange(3)]
for t in ths:
t.start()
test()
请注意,由于它是由不同的线程生成的,因此输出会有些混乱。这可以修复,但我决定不通过添加它来使这个答案的本质复杂化。