Skip to content

gh-138072: Small clarifications and phrasing improvements to asyncio HOWTO #138073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

anordin95
Copy link
Contributor

@anordin95 anordin95 commented Aug 22, 2025

Since there's only one event loop (in each thread), :mod:`!asyncio` takes care of
associating the task with the event loop for you. As such, there's no need
to specify the event loop.
Since there's only one event loop (per thread; in thread-local storage),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the previous wording was fine ("in each thread").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhm, the previous wording was certainly less clunky. This paragraph describes how asyncio automatically associates tasks with the event loop, and I'm worried the reader may be left wondering how that works when there are multiple event loop objects in the same process memory space.

I've reworked it to this, which I think flows more smoothly. Let me know!

"
asyncio automatically associates tasks with the event loop for you. In many applications there's only a single thread with the one event loop, but you can have multiple threads, each with their own event loop. Each event loop is stored in thread-local storage, making it easy for asyncio to associate tasks with the relevant loop for each thread.
"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is really overcomplicating it for the reader. asyncio is generally supposed to be "concurrency without multithreading", so having several sentences about threads is hurting that idea a little bit. We should keep it simple; a single sentence or little note about the event loop being thread-local is fine.

Copy link
Contributor Author

@anordin95 anordin95 Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Does this mean you're on board with the original change: "(per thread; in thread-local storage)", then? I agree we shouldn't go too deep into threads, and trust me I'm trying not to, but I think bringing up threads and failing to mention how asyncio still accomplishes task to event loop pairing may confuse folks.

We could do something like:

asyncio automatically associates tasks with the event loop for you. Typically there's only one event loop, so that's quite straightforward. It's uncommon, but some applications use multithreading and asyncio together, with one event loop per thread in thread-local storage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with the latter change (with a few changes, but I'll suggest those if you add it), or a variation of the original change: "per thread, using thread-local storage".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kk. Went with the latter change. Just pushed 👍

anordin95 and others added 3 commits August 23, 2025 15:04
@@ -348,8 +358,10 @@ The design intentionally trades off some conceptual clarity around usage of
``await`` for improved performance.
Each time a task is awaited, control needs to be passed all the way up the
call stack to the event loop.
That might sound minor, but in a large program with many ``await``'s and a deep
callstack that overhead can add up to a meaningful performance drag.
Then, the event loop needs to manage its data structures and work through
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, "data structure" is probably the vaguest term in all of programming. We could just say that the event loop "works through its processing logic".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ehhh, come on, I don't think it's the most vague nor more vague than "processing logic". I think it gives more insight into why there's overhead introduced -- managing internal state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, do you have a vaguer term in mind as a counterexample 😄

But seriously, I like the phrase "managing internal state" better than "manage its data structures". Can we say that instead?

Copy link
Contributor Author

@anordin95 anordin95 Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah! Hmmm. Perhaps things, routines, series of steps, computation, process, even work-chunk ;)

For reference, I do genuinely feel data structures is fairly clear (even though it's clear we disagree!). To me, it refers to things like queues, trees, hash tables, sets, etc. as opposed to procedural logic. And in this case, particularly ones that need to maintain some invariant as they perform operations.

But, yes, happy to go with that change 👍

:mod:`!asyncio` automatically associates tasks with the event loop for you.
Typically there's only one event loop, so that's quite straightforward.
It's uncommon, but some applications use multithreading and :mod:`!asyncio`
together, where there's one event loop per thread, stored in thread-local
Copy link
Contributor

@kumaraditya303 kumaraditya303 Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"stored in thread-local" is unnecesary implemenetation detail for the reader, just per thread seems sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See discussion here: #138073 (comment)

@@ -251,6 +253,10 @@ different ways::
In a crucial way, the behavior of ``await`` depends on the type of object
being awaited.

^^^^^^^^^^
await task
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await task
Awaiting Tasks

Copy link
Contributor Author

@anordin95 anordin95 Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the original for two reasons.

The titles come from the references made in the broader await section intro.

And they fit more naturally in the broader table of contents:

image

@@ -282,6 +288,10 @@ This is a basic, yet reliable mental model.
In practice, the control handoffs are slightly more complex, but not by much.
In part 2, we'll walk through the details that make this possible.

^^^^^^^^^^^^^^^
await coroutine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await coroutine
Awaiting Coroutines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting review docs Documentation in the Doc dir skip news
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

3 participants