Thread Safety in Python: What Changes with Free-Threaded Python

06.08.2026

Annika Rudolph

Annika Rudolph

Software Engineer

Introduction

For a long time, Python was not associated with true parallel thread execution. The main reason for this is the Global Interpreter Lock, or GIL for short. In CPython, it prevented multiple threads from executing Python bytecode simultaneously.
In practice, this had two consequences:
First, thread safety in Python was often given less conscious consideration than in other programming languages—after all, nothing could go wrong anyway because of the GIL. Second, Python was frequently not even considered for problems requiring lightweight, true multithreading. With Free-Threaded Python, this situation is changing. The Free-Threading build was initially introduced on an experimental basis with Python 3.13; with Python 3.14, its experimental status has been lifted. This makes it clear: Python without the GIL is here to stay.
As a result, Python is not only gaining importance for tasks that can be parallelized—the question of how safe Python code is under true parallel execution is also becoming more relevant.

For development teams, this is more than just a technical detail: When multiple threads access shared data simultaneously, bugs arise that are difficult to reproduce and even harder to debug. Consequently, coordination between threads becomes significantly more important. As a result, thread safety is taking center stage in modern Python software architectures.

Multithreading in Python: What Are the Use Cases?

Threads are lighter-weight than processes because they share memory and an interpreter. For this reason, they are primarily used when tasks are closely related but can still be processed in parallel.

Typical use cases for parallelization include:

  • Request handling on servers
  • I/O-intensive tasks (e.g., network or file access)
  • Worker models for computationally intensive tasks
  • Event and monitoring pipelines
  • Fine-grained parallel processing

In general, the well-known asyncio library is suitable for I/O-intensive tasks such as network and file access, or request handling on servers that require only a single CPU. As soon as tasks are to be executed simultaneously on multiple CPUs (for data processing, image processing, or mathematical calculations), or if cooperative waiting with `wait` conditions is not to be used, free threading is the right choice.

As soon as multiple threads modify shared objects, clear protection mechanisms are essential.

 

What does "thread safety" mean?

Thread safety means that a program functions correctly even when multiple threads are running simultaneously and accessing shared data or resources.

The key guideline is:
Shared, mutable objects and non-atomic operations must be carefully examined.

A key risk is what are known as race conditions. These occur when the result of a program depends on the order in which threads are executed. Seemingly simple operations that internally consist of multiple steps are particularly susceptible.

A simple example is incrementing a counter. Even though this operation appears atomic at first glance, Python internally performs several individual steps: read, modify, and write back. However, if multiple threads access and modify the same counter simultaneously, the result can be unpredictable—some updates are applied, while others are simply lost, depending on which thread performs which individual step and when.

The Most Important Mechanisms for Python Thread Safety

Thread safety is achieved through synchronization mechanisms. These mechanisms serve several purposes:

  • They limit concurrent access to resources
  • They make multiple statements “atomic” from a thread’s perspective
  • They enable coordination between threads based on states

The following section presents the most important tools from the threading and queue libraries.

1. Locks and RLocks: Protecting Shared State

A `threading.Lock` ensures that a section of code is executed by only one thread at a time. This is particularly important when multiple threads modify the same state, such as a counter or an object.

A quick note: Some operations on lists, dictionaries, or sets are atomic—it’s worth taking a closer look at the documentation here. When in doubt, however, it’s better to be safe than sorry.

 

Locks can be managed manually using `acquire()` and `release()`. However, it is recommended to use them as context managers (with `with`), since the lock is then automatically released.

A `threading.RLock` (reentrant lock) can be acquired multiple times by the same thread. This is helpful when nested functions require the same lock. Important: The lock must be released exactly as many times as it was acquired.
RLocks are particularly relevant in recursive or nested sequences, though they are somewhat “more expensive” than simple locks.

 

2. Queue: Messages Instead of Shared Data Structures

queue.Queue is a thread-safe implementation for ordered message passing. It is suitable when threads need to exchange tasks, events, or data.

 

Instead of securing shared lists with their own locks, threads can communicate via queues. This significantly reduces complexity and the likelihood of errors.

This approach is particularly useful in the following scenarios:

  • Event processing
  • Background jobs
  • Monitoring pipelines
  • Producer-consumer scenarios

3. Events, Barriers, Conditions, and Semaphores

Thread safety isn't just about protecting data. Often, threads also need to be coordinated—for example, in multi-stage pipelines or in consumer-producer scenarios.
The threading library offers various implementations:

A `threading.Event` serves as a simple signal between threads, such as for start permissions, shutdown, or pause-resume mechanisms. A `threading.Barrier` ensures that multiple threads reach a common synchronization point before continuing.

A `threading.Condition` helps when threads need to wait for specific conditions. A `threading.Semaphore` limits the number of concurrent accesses, for example, to database connections, file handles, or other limited resources.

While locks protect individual objects (i.e., ensure data synchronization), these mechanisms help ensure the integrity of the overall state—that is, they provide runtime synchronization.
Typically, they are created in the main thread and passed to the worker threads.

What Teams Should Review Now

Anyone who uses Python strategically should systematically check existing codebases for thread safety. Key questions to ask are:

  • Which mutable objects are shared by multiple threads?
  • Are there any non-atomic operations on shared state?
  • Do resources (e.g., database connections) need to be limited?
  • Do threads need to be actively coordinated?
  • Are the libraries used compatible with Free-Threaded Python? A compatibility tracker is available here, for example.

Thread safety should not be treated as an after-the-fact bug fix, but rather as an integral part of clean software architecture.

Important Note on AI-Generated Code

Even AI-generated code is not automatically thread-safe. Developers remain responsible for verifying the safety and correctness of such solutions.

 

Conclusion: More parallel processing—more responsibility

With Free-Threaded Python starting with version 3.13 and its stabilization in 3.14, multithreading is becoming significantly more important.

The most important guideline remains:
Consciously identify shared, mutable state, protect critical sections, and coordinate threads properly.

The `threading` library offers a range of proven tools for this purpose. One practical detail: The `logging` library is thread-safe by default.

From the perspective of modern software development, it is crucial that code not only function under ideal conditions but also remain reliable under load, in parallel environments, and in real-world operation.

At the same time, free threading makes Python more attractive for new application areas—and development teams should not hesitate to actively take advantage of these opportunities.

Annika Rudolph
Annika Rudolph
Software Engineer

Questions about the article?

Contact Us