Экземпляр класса совместного использования Python среди потоков

У меня есть класс, который загружает в память все ресурсы, необходимые для моего приложения (в основном изображения).

Затем нескольким потокам необходимо получить доступ к этим ресурсам через этот класс. Я не хочу, чтобы каждый экземпляр перезагружал все ресурсы, поэтому я решил использовать шаблон Singleton. Я сделал это так:

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

Это работает нормально, пока я не использую несколько потоков. Но с несколькими потоками каждый поток имеет другой экземпляр. Таким образом, используя 4 потока, каждый из них создает новый экземпляр. Я хочу, чтобы все потоки использовали один и тот же экземпляр этого класса, чтобы ресурсы загружались в память только один раз.

Я также пытался сделать это в том же модуле, где определен класс, но вне определения класса:

def getDataContainer():
    global dataContainer
    return dataContainer

dataContainer = DataContainer()

но каждый поток по-прежнему имеет свой собственный экземпляр.

Я новичок в Python, если это неправильный подход, пожалуйста, дайте мне знать, я ценю любую помощь.


person user2078645    schedule 23.02.2014    source источник
comment
Ваше использование Singleton кажется подходящим. Пожалуйста, опубликуйте остальную часть кода этого класса. Я пока не вижу в этом ничего плохого.   -  person Javier    schedule 24.02.2014
comment
Для многопоточности вы, вероятно, захотите передать один и тот же экземпляр объекта каждому потоку. В противном случае вам может потребоваться зарегистрировать свой экземпляр глобально.   -  person Will    schedule 24.02.2014
comment
@Will Это именно то, что я пытаюсь сделать;) Можете ли вы указать мне правильное направление, как это сделать?   -  person user2078645    schedule 25.02.2014
comment
Передайте его в конструкторе каждого класса потока, но обязательно заблокируйте все, что может измениться. Подробности смотрите в модуле потоковой передачи Python. Это слишком сложно, чтобы уместиться в один комментарий.   -  person Will    schedule 25.02.2014
comment
Попробуйте выполнить всю инициализацию в __new__, удерживая блокировку.   -  person Janne Karila    schedule 26.02.2014
comment
Спасибо за совет, при первой возможности проверю и отчитаюсь   -  person user2078645    schedule 27.02.2014
comment
@JanneKarila Попробовал ваш подход. Выполнение инициализации в методе new не помогает. Независимо от того, делаю ли я dc = DataContainter() или использую getDataContainer(), каждый поток по-прежнему имеет свой экземпляр с уникальным идентификатором.   -  person user2078645    schedule 02.03.2014
comment
@Буду ли я использовать параллельный Python, поэтому я не создаю потоки, а передаю задачи в потоки, такие как job_server.submit( ... ) . Я не могу найти способ передать объект через метод отправки   -  person user2078645    schedule 02.03.2014
comment
Таким образом, вы фактически используете несколько процессов, а не потоков. Пожалуйста, уточните вопрос.   -  person Janne Karila    schedule 02.03.2014
comment
@JanneKarila, параллельный питон больше похож на сельдерей. Он обрабатывает пулы потоков и просто предоставляет вам API для выполнения удаленных пакетных вызовов.   -  person Will    schedule 03.03.2014
comment
@user2078645 user2078645 наверняка вы передаете свой объект в качестве одного из аргументов удаленной функции?   -  person Will    schedule 03.03.2014


Ответы (1)


Чтобы расширить комментарий @Will, если «общий объект» создается родителем, а затем передается в каждый поток, все потоки будут совместно использовать один и тот же объект.

(Что касается процессов, см. multiprocessing.Manager. class, которые напрямую поддерживают совместное использование состояния, в том числе с модификациями.)

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'

Выход:

<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

Обратите внимание, что идентификаторы потоков разные, но оба они используют один и тот же экземпляр SharedObj с адресом памяти, заканчивающимся на 440.

person johntellsall    schedule 31.05.2014
comment
это работает, если есть другие методы наряду с init и run? - person Nitwit; 05.02.2021