11

I have a class, that loads all resources into memory that are needed for my application (mostly images).

Then several threads need to access these resources through this class. I don't want every instance to reload all resources, so I thought I use the Singleton Pattern. I did it like this:

class DataContainer(object):
    _instance = None
    _lock = threading.Lock()
    _initialised = True

    def __new__(cls, *args, **kwargs):
        with cls._lock:
            if not cls._instance:
                cls._initialised = False
                cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self, map_name = None):

        # instance has already been created
        if self._initialised:
            return

        self._initialised = True

        # load images

This works fine, as long as I am not using multiple threads. But with multiple Threads every thread has a different instance. So using 4 threads, they each create a new instance. I want all threads to use the same instance of this class, so the resources are only loaded into memory once.

I also tried to do this in the same module where the class is defined, but outside the class definition:

def getDataContainer():
    global dataContainer
    return dataContainer

dataContainer = DataContainer()

but every thread still has its own instance.

I am new to python, if this is the wrong approach plz let me know, I appreciate any help

11
  • Your use of a Singleton seems appropriate. Please post the rest of the code of this class. I cannot see anything wrong with it so far. Commented Feb 23, 2014 at 20:30
  • 1
    For multithreaded you probably want to pass the same instance of the object to each thread. Failing that, you might need to register your instance globally. Commented Feb 23, 2014 at 20:56
  • @Will That is exactly what I am trying to do ;) Can you point me in the right direction on how to do that? Commented Feb 24, 2014 at 20:29
  • Pass it in the constructor of each thread class but make sure to "lock" anything that might change. See the python threading module for details. It's a but too complicated to fit into one comment. Commented Feb 25, 2014 at 13:41
  • Try doing all initialization in __new__ while holding the lock. Commented Feb 26, 2014 at 7:57

1 Answer 1

15

To expand on @Will's comment, if a "shared object" is created by the parent, then passed in to each thread, all threads will share the same object.

(With processes, see the multiprocessing.Manager class, which directly support sharing state, including with modifications.)

import threading, time


class SharedObj(object):
    image = 'beer.jpg'


class DoWork(threading.Thread):
    def __init__(self, shared, *args, **kwargs):
        super(DoWork,self).__init__(*args, **kwargs)
        self.shared = shared

    def run(self):
        print threading.current_thread(), 'start'
        time.sleep(1)
        print 'shared', self.shared.image, id(self.shared)
        print threading.current_thread(), 'done'


myshared = SharedObj()
threads = [ DoWork(shared=myshared, name='a'), 
            DoWork(shared=myshared, name='b')
]
for t in threads:
    t.start()
for t in threads:
    t.join()
print 'DONE'

Output:

<DoWork(a, started 140381090318080)> start
<DoWork(b, started 140381006067456)> start
shared beer.jpg shared140381110335440
 <DoWork(b, started 140381006067456)> done
beer.jpg 140381110335440
<DoWork(a, started 140381090318080)> done
DONE

Note that the thread IDs are different, but they both use the same SharedObj instance, at memory address ending in 440.

Sign up to request clarification or add additional context in comments.

1 Comment

does this work if there are other methods alongside init and run too?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.