Daemon threads are a regular source of pain in core development and have been for years. I’d like to get rid of them. The impact on the Python community is the deciding factor; I suspect low impact but may be wrong. Your feedback will help.
Proposal
At the very least I’d like to do the following:
- [3.12+] (or earlier) clarify docs about daemon threads and add a warning (see gh-125857)
- [3.14] (or earlier) add a deprecation note to the docs (“soft deprecation”)
If the impact on users is not too much then I’d like to move toward getting rid of daemon threads entirely:
- [3.14] emit a deprecation warning when
Thread(..., daemon=True)
is called orthread.daemon
is set to True (thread.setDaemon()
is already deprecated) - [3.27] remove support for daemon threads
Motivation
- daemon threads are a consistent cause of crashes in CPython
- daemon threads add complexity to key parts of CPython’s runtime
- the docs have not been clear about the use cases for daemon threads, that they should be avoided, and the alternatives
Impact
Community usage:
TBD
Existing users of daemon threads can generally update their code to get the same effect from non-daemon threads. See gh-125857 for examples.
Context
(expand)
What Are Daemon Threads?
From the docs:
A thread can be flagged as a “daemon thread”. The significance of
this flag is that the the entire Python program exits when only
daemon threads are left. The initial value is inherited from the
creating thread. The flag can be set through the daemon property
or the daemon constructor argument.
| Note: Daemon threads are abruptly stopped at shutdown. Their
| resources (such as open files, database transactions, etc.) may
| not be released properly. If you want your threads to stop
| gracefully, make them non-daemonic and use a suitable signalling
| mechanism such as an Event.
History
Daemon threads have been a part of the threading module since it was added in 1998 (Python 1.5.1). (The low-level “thread” module was added in 1992, without daemon threads.)
Why were daemon threads part of that initial API? Mostly because Java threads had them–the threading module was essentially copied from Java’s threading API. Unfortunately, in the case of daemon threads, we eventually learned that what made sense for Java did not make sense for Python.
Note that in Python 3.12, interpreters created using Py_NewInterpreterFromConfig()
default to disallowing daemon threads. In 3.14, those created by InterpreterPoolExecutor
never allow daemon threads. Likewise for the API in PEP 734.
The Pros and Cons of Daemon Threads
In theory, daemon threads help keep low-priority background tasks from blocking application exit. This could be especially useful when your background task calls some long-running third-party non-Python function that doesn’t offer any way to interrupt it or call it for short intervals.
Outside of that case, there aren’t real benefits over the alternatives.
However, in the last 26 years (since Python 1.5), we’ve learned some of the downsides of daemon threads:
- harder to reason about what happens during finalization
- adds significant complexity to CPython thread and finalization code
- a consistent source of bugs, especially crashes, in CPython
- conceptually, “daemon” threads is easy to confuse with “daemon” processes, which are in some ways opposites
Ultimately, the cost to CPython maintenance and stability over the years has been not insignificant and continues to be so.
One issue that has been a contributing factor is that the docs do not provide a clear message about what daemon threads are for, nor that they should be avoided otherwise. Thus, people end up using them when they shouldn’t.
Daemon Threads in Other Languages
language | daemon threads |
---|---|
Java | same as Python’s |
C++ | always (explicit join) |
C | always (explicit join) |
Rust | always (explicit join) |
Go | goroutines-only |
C# | yes |
Haskell | no? |
Erlang | no? |
Clojure | always (explicit join) |
Ruby | always (explicit join) |