-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
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
base: main
Are you sure you want to change the base?
Conversation
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), |
There was a problem hiding this comment.
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").
There was a problem hiding this comment.
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.
"
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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 👍
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
@@ -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 |
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 👍
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
: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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await task | |
Awaiting Tasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await coroutine | |
Awaiting Coroutines |
📚 Documentation preview 📚: https://cpython-previews--138073.org.readthedocs.build/