Fundamentals 8 min read

Thread‑Safe Counter Implementations in Python: Single‑Thread, Fast‑Read, and Fast‑Write Approaches

This article explains how to implement simple counters in Python, demonstrates why a naïve single‑thread counter is not safe for concurrent use, and presents three thread‑safe alternatives—using a lock, leveraging the GIL with itertools.count, and a combined fast‑read/fast‑write design—along with a performance comparison.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Thread‑Safe Counter Implementations in Python: Single‑Thread, Fast‑Read, and Fast‑Write Approaches

When collecting logs or metrics, a counter is a common tool; a straightforward single‑thread implementation is shown first.

<code>class SingleThreadCounter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1
</code>

Although suitable for single‑threaded programs, this class is not thread‑safe: concurrent increments can overwrite each other, leading to lost updates.

To make the counter thread‑safe, a lock is introduced so that each increment is performed serially.

<code>import threading
class FastReadCounter:
    def __init__(self):
        self.value = 0
        self._lock = threading.Lock()

    def increment(self):
        with self._lock:
            self.value += 1
</code>

This implementation guarantees correct increments but may suffer from lock contention under heavy write load.

A lock‑free fast‑write counter exploits CPython’s Global Interpreter Lock (GIL) and itertools.count , providing lock‑free writes while using a separate lock only for reading the current value.

<code>import itertools, threading
class FastWriteCounter:
    def __init__(self):
        self._number_of_read = 0
        self._counter = itertools.count()
        self._read_lock = threading.Lock()

    def increment(self):
        next(self._counter)

    def value(self):
        with self._read_lock:
            value = next(self._counter) - self._number_of_read
            self._number_of_read += 1
        return value
</code>

Because CPython’s GIL serializes bytecode execution, the write operation does not need an explicit lock, while reads acquire a lock to compute the current count safely.

A performance test using the timeit module compares the three counters. The results (increment latency and read latency) are shown in the table below.

OPERATION

SINGLETHREADCOUNTER

FASTREADCOUNTER

FASTWRITECOUNTER

increment

176 ns

390 ns

169 ns

value

26 ns

26 ns

529 ns

The benchmark shows that SingleThreadCounter and FastReadCounter have similar read performance, while FastWriteCounter offers the fastest increment time but a slower read due to the additional lock.

Finally, a complete implementation combining all three classes is provided for reference.

<code>import itertools, threading
class Counter(object):
    """A counter that is only suitable for application without any concurrency."""
    __slots__ = ("value", "_step")
    def __init__(self, init=0, step=1):
        self.value = init
        self._step = step
    def increment(self):
        self.value += self._step

class FastReadCounter(Counter):
    __slots__ = ("value", "_lock", "_step")
    def __init__(self, init=0, step=1):
        super().__init__(init, step)
        self._lock = threading.Lock()
    def increment(self):
        with self._lock:
            self.value += self._step

class FastWriteCounter(Counter):
    __slots__ = ("_number_of_read", "_counter", "_lock", "_step")
    def __init__(self, init=0, step=1):
        self._number_of_read = 0
        self._step = step
        self._counter = itertools.count(init, step)
        self._lock = threading.Lock()
    def increment(self):
        next(self._counter)
    @property
    def value(self):
        with self._lock:
            value = next(self._counter) - self._number_of_read
            self._number_of_read += self._step
        return value
</code>
PerformancepythonConcurrencyThread SafetyGILcounter
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.