2

次のシナリオがあります。

  • マルチスレッドアプリケーション
  • 私はスレッドの作成を管理していません。これはフレームワーク(この場合はセロリ)によって管理されます
  • インスタンス化に費用がかかり、スレッドセーフではないオブジェクトがいくつかあります。それらをスレッドセーフにすることはオプションではありません。
  • オブジェクトは複数の場所でインスタンス化できますが、すでに定義されている1つのスレッドで同じオブジェクトを再インスタンス化する場合は、オブジェクトを再利用する必要があります。

私は次のパターンを思いついた:

#!/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です。いくつか質問があります。

  1. このロジックは、オブジェクトがスレッド間で共有されないことを保証しますか?
  2. このロジックは、オブジェクトがスレッド内で複数回インスタンス化されないことを保証しますか?
  3. このメモリリークはありますか?
  4. このアプローチについて一般的なコメントはありますか?上記で提案されたシナリオのより良い提案はありますか?

編集

明確にするために:私のアプリケーションはマルチスレイドですが、スレッドを作成しているのは私ではありません。フレームワークによって作成されたスレッド内で実行されるいくつかのオブジェクトを作成しているだけです。一部のオブジェクトはスレッドセーフではないため、スレッドごとに1回だけ作成する必要があります。したがってget_my_object

編集

local = threading.local()は、グローバルスコープで定義する必要があります。

4

3 に答える 3

1

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_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()
于 2012-12-21T23:24:49.140 に答える
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()
    # ...

最後に、それはあなたの例に似ていますが、よりきれいです。すべてのスレッド固有のコードを 1 か所に保持し、run関数は初期化について何も「認識」せずmy_obj、ゲッターを使用して取得し、ゲッターはオブジェクトを 1 回だけ作成します。threading.localは、データがスレッド固有であることを保証します-それがその仕事です。

そこにメモリリークの理由は見当たりません。最後に、Pythonでリークを取得するには、少し汗をかく必要があります:)

于 2012-12-21T14:15:02.193 に答える
0

これは、スレッドレベルのシングルトンを持つという私が持っていたアイデアを利用した別の答えです。それはあなたの機能を完全に取り除きますget_local_obj()。私は多くのテストを行っていませんが、これまでのところうまくいくようです。最後の箇条書きであなたが望んでいたことを文字通り実装しているため、それはあなたが望む以上のものかもしれません:

  • オブジェクトは複数の場所でインスタンス化できますが、既に定義されている 1 つのスレッドで同じオブジェクトを再インスタンス化する場合は、オブジェクトを再利用する必要があります。

#!/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()
異なるスレッドによって生成されるため、出力が多少乱雑になることに注意してください。これは修正できますが、追加してこの回答の本質を複雑にしないことにしました。
于 2012-12-22T10:46:38.323 に答える