Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在多线程环境中重用本地对象_Python_Python Multithreading - Fatal编程技术网

Python 在多线程环境中重用本地对象

Python 在多线程环境中重用本地对象,python,python-multithreading,Python,Python Multithreading,我有以下情况: 多线程应用程序 我不能控制线程的创建。这是由框架管理的(在本例中为芹菜) 我有一些对象实例化起来很昂贵,而且不是线程安全的。使它们线程安全不是一个选项 这些对象可以在多个地方实例化,但是如果我在一个已经定义的线程中重新实例化同一个对象,那么应该重用该对象 我提出了以下模式: #!/usr/bin/env python import threading import time class MyObj1: def __init__(self, name):

我有以下情况:

  • 多线程应用程序
  • 我不能控制线程的创建。这是由框架管理的(在本例中为芹菜)
  • 我有一些对象实例化起来很昂贵,而且不是线程安全的。使它们线程安全不是一个选项
  • 这些对象可以在多个地方实例化,但是如果我在一个已经定义的线程中重新实例化同一个对象,那么应该重用该对象
我提出了以下模式:

#!/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\u local\u obj
。我有几个问题:

  • 这个逻辑能保证线程之间不共享对象吗
  • 这个逻辑能保证对象在一个线程中不会被实例化多次吗
  • 这个内存会泄漏吗
  • 你对这种方法有什么一般性意见吗?对于上述场景,有什么更好的建议吗
  • 编辑 澄清一下:我的应用程序是多线程的,但创建线程的不是我。我只是创建一些对象,这些对象恰好在框架创建的线程中运行。我的一些对象不是线程安全的,所以每个线程只需要创建一次。因此,
    获取我的对象

    编辑 local=threading.local()必须在全局作用域上定义。

    这个如何

    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只创建一次对象。将保证数据是特定于线程的-这是它的工作


    我看不出有任何内存泄漏的原因。最后,您需要花一点力气才能找到python中的漏洞:)

    FWIW,这是您的代码的一个修改版本,它在某种程度上基于一个和相关问题进行了简化。但基本上还是一样的模式

    #!/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\u local\u obj()
    ,也可以做完全相同的事情:


    这里有另一个不同的答案,它利用了我关于线程级单例的想法。它完全摆脱了
    get\u local\u 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()
    

    请注意,由于输出是由不同的线程生成的,因此输出会有点混乱。这是可以解决的,但我决定不通过添加它来复杂化这个答案的本质。谢谢,但那不行。我并不是真的在应用程序中创建线程(WorkerThread不是我的)。框架是。我需要一种创建线程本地对象的通用方法。请看我的编辑。但是你创建对象的方法很有趣。为什么你要走
    尝试/例外的路线,而不是我选择的
    \uuu dict\uuu
    路线?有什么特别的优势吗?我只是不喜欢访问那些属于私人的领域。我明白你的意思。但是,由于我使用的是可变键,而不是固定属性,因此无法使用
    try/except
    方法。执行
    obj=local[key]
    local[key]=obj
    会引发
    TypeError:“thread.\u local”对象不支持项目分配
    x=threading.local()
    ,之后
    print x.hello
    会引发
    属性错误,但
    x.hello=100;print x.hello
    打印值,这是您使用它的方式。我喜欢
    setattr/getattr
    。它比使用
    \uuuu dict\uuuu
    更简洁,并且允许使用变量属性名称,这正是我所需要的。您的代码(以及我的代码和Jakub M的代码)的问题在于它不起作用,因为我发现了困难的方法。我们只需要在主线程的全局作用域上实例化一次
    threadLocal=threading.local()
    。请参阅以下答案:。我将相应地修改我的问题。是的,属性获取/设置方式似乎更简洁。我想知道
    theading.local()
    的作用域,幸运的是这很容易修复,感谢您指出了这个问题。我有另一个想法,我正试图找出如何实现…如果它成功了,我将发布另一个答案。
    #!/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()