Thread-safe singleton in Python

Boris HUISGEN
Boris HUISGEN
Thread-safe singleton in Python

The singleton is probably the most used design pattern to limit usage of resources like the number of connections to databases or any other services. Its implementation is relatively easy but it could be difficult to keep thread-safety.

Thread safety is generally solved by a synchronization system like a lock which Python owns with its primitive Lock from the threading package but some tricks are required to complete thread safety.

Implementation

 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)

The trick is to use a lock combined to another object which is going to keep the shared instance of your singleton class.

Test

To test that it works as needed let’s try it in a Python console:

>>> 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

As you can see the instances a and b are the same.

Boris HUISGEN
Boris HUISGEN
Blog owner
  • #python