Singleton thread-safe en Python

Boris HUISGEN
Boris HUISGEN
Singleton thread-safe en Python

Le singleton est probablement le design pattern le plus utilisé pour limiter la consommation des ressources comme le nombre de connexions aux bases de données ou tout autre service. Son implémentation est relativement simple mais elle peut rapidement devenir source d’ennuis si le code doit être thread-safe.

Un code thread-safe est généralement garanti par utilisation d’un système de synchronisation comme un verrou dont Python possède grâce à sa primitive Lock du module threading, mais une autre astuce est nécessaire pour un support thread-safe.

Implémentation

 1import logging
 2import threading
 3
 4class MySingleton(object):
 5    _instance: object = None
 6    _lock: threading.Lock = threading.Lock()
 7
 8    def __new__(cls):
 9        """Create a new instance"""
10        if MySingleton._instance is None:
11            with MySingleton._lock:
12                if MySingleton._instance is None:
13                    MySingleton._instance = super(MySingleton, cls).__new__(cls)
14                    MySingleton._instance._initialized = False
15
16        return MySingleton._instance
17
18    def __init__(self):
19        """Initialize the instance"""
20        if self._initialized is False:
21            self._logger = logging.getLogger(self.__class__.__name__)
22            self._count = 0
23            self._initialized = True
24
25        self._logger.debug("using instance: %s" % self._instance)
26
27    def increment(self):
28        """Increment the counter"""
29        self._count += 1
30
31        self._logger.debug("count = %d" % self._count)

L’astuce est donc d’utiliser un verrou combiné à un autre objet qui va conserver l’instance partagée de votre classe singleton.

Test

Pour tester son fonctionnement, lancez une console Python:

>>> from code import MySingleton
>>> logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()])
>>> a = MySingleton()
DEBUG:MySingleton:using instance: <__main__.MySingleton object at 0x7f8bee80c550>
>>> b = MySingleton()
DEBUG:MySingleton:using instance: <__main__.MySingleton object at 0x7f8bee80c550>
>>> a.increment()
DEBUG:MySingleton:count = 1
>>> a.increment()
DEBUG:MySingleton:count = 2
>>> b.increment()
DEBUG:MySingleton:count = 3

Comme vous pouvez le voir, les instances a et b sont identiques.

Boris HUISGEN
Boris HUISGEN
Auteur du blog
  • #python