Discussion thread for: http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/

163 views
Skip to first unread message

Justin Mayfield

unread,
Oct 30, 2016, 1:29:03 PM10/30/16
to python-tulip
I'm curious what a more localized discussion about this blog post looks like.  Personally I find it difficult to sympathize with many of the arguments and have been a very happy user of modern python and asyncio for quite a while.

Guido van Rossum

unread,
Oct 30, 2016, 1:33:51 PM10/30/16
to Justin Mayfield, python-tulip
Armin is a great programmer but he is a well-known curmudgeon when it
comes to anything related to Python 3.
--
--Guido van Rossum (python.org/~guido)

Yury Selivanov

unread,
Oct 30, 2016, 1:53:55 PM10/30/16
to Justin Mayfield, python-tulip
I think there are some valid, as well as not so valid, points in that blog post. One thing for sure: we need to restructure the documentation. Anyways, I’ll try to go through the blog post in detail and create issues to address some things.

Yury

Guido van Rossum

unread,
Oct 30, 2016, 1:58:56 PM10/30/16
to Yury Selivanov, Justin Mayfield, python-tulip
Regarding the documentation, I doubt we can get it in shape just by
filing issues. We should really just have a tutorial written by
someone with a good understanding of asyncio and writing skills.

I wonder how enlightening the chapter by A. Jesse Jiryu Davis here
really is for beginners?
http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html
(my name is on it because I wrote the original code -- Jesse wrote all
the text and rewrote the code several times over :-).

Yury Selivanov

unread,
Oct 30, 2016, 2:27:54 PM10/30/16
to gu...@python.org, Justin Mayfield, python-tulip

> On Oct 30, 2016, at 1:58 PM, Guido van Rossum <gu...@python.org> wrote:
>
> Regarding the documentation, I doubt we can get it in shape just by
> filing issues. We should really just have a tutorial written by
> someone with a good understanding of asyncio and writing skills.

Yeah, I agree. I don’t want to promise anything, but this is something I wanted to do for a long time now. If no one tackles this in the next couple of months, I’ll try to start the process myself.

>
> I wonder how enlightening the chapter by A. Jesse Jiryu Davis here
> really is for beginners?
> http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html
> (my name is on it because I wrote the original code -- Jesse wrote all
> the text and rewrote the code several times over :-).

I think it’s an excellent piece, but maybe it’s a tad more detailed than what we need for asyncio docs. IMO the opening page of asyncio docs should cover the following:

1. Why asyncio - one paragraph.
2. What is event loop - one p.
3. What is a coroutine - one p.
4. asyncio essentials: get_event_loop, run_until_complete, close, create_task, gather, open_connection, start_server.
5. Advanced topics: should I pass event loop explicitly? etc

Speaking about APIs and passing working with the loop. One thing that people find attractive in curio is that it doesn’t really focus on the loop. You start your initial coroutine with the loop (called “kernel” in curio) and that’s it.

What if we could do the following:

1. Provide an “asyncio.run(coro)” function to run your "main()” coroutine program and take care of everything loop-related for you.

“asyncio.run” should create the loop, run the coroutine, cleanup asynchronous generators and unfinished tasks, close the loop etc.

2. There are some APIs that can’t be called without a loop: run_in_executor, sockets ops etc. What if we add “asyncio.run_in_background” coroutine that would get the event loop and call run_in_executor on it?

3. In 3.7 we can add a Socket-like object with asynchronous “recv”, “send”, etc coroutines. Turns out people want this, as it’s more convenient to use than loop.sock_sendall. Using streams is of course better, but sometimes you want to quickly make your existing program async without a full rewrite.

Guido, what do you think?

Yury

Guido van Rossum

unread,
Oct 30, 2016, 2:43:09 PM10/30/16
to Yury Selivanov, Justin Mayfield, python-tulip
On Sun, Oct 30, 2016 at 11:27 AM, Yury Selivanov <yseli...@gmail.com> wrote:
>
>> On Oct 30, 2016, at 1:58 PM, Guido van Rossum <gu...@python.org> wrote:
>>
>> Regarding the documentation, I doubt we can get it in shape just by
>> filing issues. We should really just have a tutorial written by
>> someone with a good understanding of asyncio and writing skills.
>
> Yeah, I agree. I don’t want to promise anything, but this is something I wanted to do for a long time now. If no one tackles this in the next couple of months, I’ll try to start the process myself.
>
>>
>> I wonder how enlightening the chapter by A. Jesse Jiryu Davis here
>> really is for beginners?
>> http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html
>> (my name is on it because I wrote the original code -- Jesse wrote all
>> the text and rewrote the code several times over :-).
>
> I think it’s an excellent piece, but maybe it’s a tad more detailed than what we need for asyncio docs. IMO the opening page of asyncio docs should cover the following:
>
> 1. Why asyncio - one paragraph.
> 2. What is event loop - one p.
> 3. What is a coroutine - one p.
> 4. asyncio essentials: get_event_loop, run_until_complete, close, create_task, gather, open_connection, start_server.

IMO this focuses on the loop too much. We should explain more on how
to use coroutines.

> 5. Advanced topics: should I pass event loop explicitly? etc
>
> Speaking about APIs and passing working with the loop. One thing that people find attractive in curio is that it doesn’t really focus on the loop. You start your initial coroutine with the loop (called “kernel” in curio) and that’s it.

Isn't the equivalent in asyncio just as easy? Start your initial
coroutine with asyncio.get_event_loop().run_until_complete() and
that's it.

How does curio open a connection? How does it start serving? Asyncio
requires the loop for those. Is the loop implicit in curio?

> What if we could do the following:
>
> 1. Provide an “asyncio.run(coro)” function to run your "main()” coroutine program and take care of everything loop-related for you.

So that's just asyncio.get_event_loop().run_until_complete(coro)? I
worry that the shortness of the call will make people call this from
inside coroutines or callbacks.

> “asyncio.run” should create the loop, run the coroutine, cleanup asynchronous generators and unfinished tasks, close the loop etc.

I'd be less opposed if it was called asyncio.main(). That makes it
clear that you run this one, inside your own main.

> 2. There are some APIs that can’t be called without a loop: run_in_executor, sockets ops etc. What if we add “asyncio.run_in_background” coroutine that would get the event loop and call run_in_executor on it?

Well in theory you could add asyncio.foo() calls for every loop
method. I'm not sure I'm excited about that. Nor about using different
names.

> 3. In 3.7 we can add a Socket-like object with asynchronous “recv”, “send”, etc coroutines. Turns out people want this, as it’s more convenient to use than loop.sock_sendall. Using streams is of course better, but sometimes you want to quickly make your existing program async without a full rewrite.

I'd question *why* people want sockets. Perhaps it's really just a
wrapper around a StreamReader/Writer pair? Can a curio socket wrap a
subprocess? A pipe? A tty device?

> Guido, what do you think?

I think it'll blow over. I certainly think for 3.6 we should not
change any APIs but focus on making the docs much stronger.

But I'm also trying to extract myself from asyncio development; I'm
more focused on mypy ATM (if you hadn't noticed :-).

Yury Selivanov

unread,
Oct 30, 2016, 3:02:52 PM10/30/16
to gu...@python.org, Justin Mayfield, python-tulip

> On Oct 30, 2016, at 2:42 PM, Guido van Rossum <gu...@python.org> wrote:
>
> On Sun, Oct 30, 2016 at 11:27 AM, Yury Selivanov <yseli...@gmail.com> wrote:
>>
>>> On Oct 30, 2016, at 1:58 PM, Guido van Rossum <gu...@python.org> wrote:
>>>
>>> Regarding the documentation, I doubt we can get it in shape just by
>>> filing issues. We should really just have a tutorial written by
>>> someone with a good understanding of asyncio and writing skills.
>>
>> Yeah, I agree. I don’t want to promise anything, but this is something I wanted to do for a long time now. If no one tackles this in the next couple of months, I’ll try to start the process myself.
>>
>>>
>>> I wonder how enlightening the chapter by A. Jesse Jiryu Davis here
>>> really is for beginners?
>>> http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html
>>> (my name is on it because I wrote the original code -- Jesse wrote all
>>> the text and rewrote the code several times over :-).
>>
>> I think it’s an excellent piece, but maybe it’s a tad more detailed than what we need for asyncio docs. IMO the opening page of asyncio docs should cover the following:
>>
>> 1. Why asyncio - one paragraph.
>> 2. What is event loop - one p.
>> 3. What is a coroutine - one p.
>> 4. asyncio essentials: get_event_loop, run_until_complete, close, create_task, gather, open_connection, start_server.
>
> IMO this focuses on the loop too much. We should explain more on how
> to use coroutines.
>
>> 5. Advanced topics: should I pass event loop explicitly? etc
>>
>> Speaking about APIs and passing working with the loop. One thing that people find attractive in curio is that it doesn’t really focus on the loop. You start your initial coroutine with the loop (called “kernel” in curio) and that’s it.
>
> Isn't the equivalent in asyncio just as easy? Start your initial
> coroutine with asyncio.get_event_loop().run_until_complete() and
> that's it.

You also should call loop.close(), which adds try..except around your loop.run_until_complete code. You also should await on loop.shutdown_asyncgens in 3.6. Those things add up.

>
> How does curio open a connection? How does it start serving? Asyncio
> requires the loop for those. Is the loop implicit in curio?

Well, in asyncio you can just “await asyncio.open_connection()” without passing a loop explicitly.

And that’s how curio does this too - you always use coroutines to do *everything*. There is API to reliably get the loop that runs your current code (‘asyncio.get_event_loop’ should be fixed a little bit in my opinion, but we already had this discussion here: https://github.com/python/asyncio/pull/355).

So in curio the loop is implicit, and I think it’s actually a good design decision. Because the “root” of your program is always a coroutine, and you can reliably get the current loop, you don’t need to pass it around explicitly.

I think that even with the current asyncio design we should aim for the similar user experience. We already have “asyncio.open_connection” that doesn’t require the loop. We can add “asyncio.run_in_executor” (as opposed to “loop.run_in_executor") so that there is one less API that requires you to care about the loop.

>
>> What if we could do the following:
>>
>> 1. Provide an “asyncio.run(coro)” function to run your "main()” coroutine program and take care of everything loop-related for you.
>
> So that's just asyncio.get_event_loop().run_until_complete(coro)? I
> worry that the shortness of the call will make people call this from
> inside coroutines or callbacks.
>
>> “asyncio.run” should create the loop, run the coroutine, cleanup asynchronous generators and unfinished tasks, close the loop etc.
>
> I'd be less opposed if it was called asyncio.main(). That makes it
> clear that you run this one, inside your own main.

I’d be OK with “asyncio.main".

>
>> 2. There are some APIs that can’t be called without a loop: run_in_executor, sockets ops etc. What if we add “asyncio.run_in_background” coroutine that would get the event loop and call run_in_executor on it?
>
> Well in theory you could add asyncio.foo() calls for every loop
> method. I'm not sure I'm excited about that. Nor about using different
> names.

Yeah, let’s forget about using different names — a bad idea.

I’ve just looked through events.py. I think we may only need to add “asyncio.run_in_executor(func)”, and maybe “asyncio.create_task(coroutine)” functions. With that it should be possible to have almost the same “loopless” experience in asyncio as with curio:

async def program():
await asyncio.run_in_executor(func)
await asyncio.open_connection(…)
task = asyncio.create_task(…)
asyncio.main(program())


>
>> 3. In 3.7 we can add a Socket-like object with asynchronous “recv”, “send”, etc coroutines. Turns out people want this, as it’s more convenient to use than loop.sock_sendall. Using streams is of course better, but sometimes you want to quickly make your existing program async without a full rewrite.
>
> I'd question *why* people want sockets.

Just more familiar API I think.

> Perhaps it's really just a
> wrapper around a StreamReader/Writer pair? Can a curio socket wrap a
> subprocess? A pipe? A tty device?

Curio socket is a very thin wrapper on top of python socket and whatever non-blocking stuff python allows to do. The API is essentially the same, with all blocking methods made coroutines.

I think people find Curio streams a bit more intuitive because they are just one object, not a pair of reader/writer.

Yury

Luciano Ramalho

unread,
Oct 30, 2016, 3:28:53 PM10/30/16
to Yury Selivanov, Guido van Rossum, Justin Mayfield, python-tulip
I found asyncio really daunting. Writing about it in Fluent Python was
by far the hardest part in the book -- and my goal was really modest,
just explain some very, very basic examples. I was glad to have Victor
Stinner review that chapter in my book, so no major problems were
reporded by readers so far, and several readers have written nice
things about that chapter.

BTW, if any of you would like a courtesy copy of the e-book, let me
know and I'll ask O'Reilly.

I also believe we should have some basic asyncio HTTP support in the
standard library. Nothing as fancy or full-featured as aiohttp --
which is great -- but just some basic functionality so that the
documentation can offer better examples, closer to what most users
will want to do with asyncio (which is not dealing with raw sockets or
inventing their own network protocols).

Cheers,

Luciano
--
Luciano Ramalho
| Author of Fluent Python (O'Reilly, 2015)
| http://shop.oreilly.com/product/0636920032519.do
| Technical Principal at ThoughtWorks
| Twitter: @ramalhoorg

Aymeric Augustin

unread,
Oct 30, 2016, 3:50:05 PM10/30/16
to Justin Mayfield, python-tulip
Hello,

As far as I understand, Armin’s starting point was “I want to understand everything about asyncio in order to support it well in Sentry”. This is higher bar than “I want to write decent asyncio code to get $X done”. I suspect that explains the absurd list of “things that you need to know” he opens his piece with. Many people have written reasonable asyncio code without being aware half of these even exist.

Essentially Armin brings up two features that (per his analysis) aren’t available or easy to implement in asyncio:
1. getting a reference to the currently running event loop from a coroutine,
2. "async context propagation”.

I don’t feel qualified to discuss these because I’ve never needed them but I trust Armin to bring them up for a good reason. I’m curious whether Sentry is a special case or whether they’re more generally useful. (I’m reporting this here so people can get the main information without slogging through a non-trivial amount of negativity.)

Improvements to the documentation were discussed in this thread already. I learnt asyncio with PEP3156 before the effort on the documentation started and I suspect some concepts are still better explained in the PEP than in the docs. Perhaps taking some ideas — especially context about why APIs exist — from the PEP to the docs could bring some quick wins? Armin suggest he struggled to understand why some of the concepts exist. He barely mentions compatibility with other async frameworks, which was a big requirement in the design of asyncio. (Disclaimer: I don’t know the latest version of the asyncio docs all that well.)

Finally, I’m sad to see smart people who read neither PEP3156 nor dabeaz’ plans for curio say “curio’s 2x faster” without qualification… Oh well…

Hope this helps,

--
Aymeric.

Guido van Rossum

unread,
Oct 30, 2016, 5:36:22 PM10/30/16
to Luciano Ramalho, Yury Selivanov, Justin Mayfield, python-tulip
On Sun, Oct 30, 2016 at 12:28 PM, Luciano Ramalho <luc...@ramalho.org> wrote:
> I found asyncio really daunting. Writing about it in Fluent Python was
> by far the hardest part in the book -- and my goal was really modest,
> just explain some very, very basic examples. I was glad to have Victor
> Stinner review that chapter in my book, so no major problems were
> reporded by readers so far, and several readers have written nice
> things about that chapter.

We definitely have a documentation problem. I kind of ran out of steam
after writing and negotiating so much of the code and the PEP, and let
others finish the docs.

But it would still be interesting to hear in more detail (if you
remember at all) which *concepts* are daunting.

> BTW, if any of you would like a courtesy copy of the e-book, let me
> know and I'll ask O'Reilly.

I have one, and it's great! Thanks for writing it.

> I also believe we should have some basic asyncio HTTP support in the
> standard library. Nothing as fancy or full-featured as aiohttp --
> which is great -- but just some basic functionality so that the
> documentation can offer better examples, closer to what most users
> will want to do with asyncio (which is not dealing with raw sockets or
> inventing their own network protocols).

But if you want HTTP, you really should use aiohttp, not some crappy
version that we might supply in the stdlib. There's nothing
dishonorable about using a 3rd party package!

--Guido

Donald Stufft

unread,
Oct 30, 2016, 6:00:24 PM10/30/16
to gu...@python.org, Luciano Ramalho, Yury Selivanov, Justin Mayfield, python-tulip

On Oct 30, 2016, at 5:36 PM, Guido van Rossum <gu...@python.org> wrote:

But if you want HTTP, you really should use aiohttp, not some crappy
version that we might supply in the stdlib. There's nothing
dishonorable about using a 3rd party package!


Maybe it would make sense to use aiohttp or something to explain the concepts of AsyncIO instead of restricting it to just what’s in the standard library? We have pip shipped w/ Python now and the venv module exists, so for most people installing aiohttp into a virtual environment or something to play with asyncio shouldn’t be a big deal.

To be clear, I don’t mean try to extensively document aiohttp, but rather use it as a real-ish world introduction to asyncio for folks.


Donald Stufft



Yury Selivanov

unread,
Oct 30, 2016, 6:03:04 PM10/30/16
to gu...@python.org, Justin Mayfield, python-tulip

On Oct 30, 2016, at 3:02 PM, Yury Selivanov <yseli...@gmail.com> wrote:

I’ve just looked through events.py.  I think we may only need to add “asyncio.run_in_executor(func)”, and maybe “asyncio.create_task(coroutine)” functions.  With that it should be possible to have almost the same “loopless” experience in asyncio as with curio:

async def program():
   await asyncio.run_in_executor(func)
   await asyncio.open_connection(…)
   task = asyncio.create_task(…)
asyncio.main(program())

Never mind 'asyncio.create_task', we already have ensure_future.

Yury

Guido van Rossum

unread,
Oct 30, 2016, 6:11:49 PM10/30/16
to Donald Stufft, Luciano Ramalho, Yury Selivanov, Justin Mayfield, python-tulip
Such a tutorial would be fine as a HOWTO (in
https://docs.python.org/3/howto/index.html) but IMO not in the stdlib
docs for asyncio itself (there could be a pointer to the HOWTO doc of
course). Then again the stdlib docs don't need to be a tutorial.

Skimming Armin's blog it seems one problem is that he treats all
concepts equally. E.g. you don't really need to know about policies to
get started, nor about the details of how event loops are bound to
threads, nor about how to deal with multiple event loops at all, or
how to survive if the event loop is not bound to a thread. (Maybe he
read asyncio's own code to try to learn how to use it? or aiohttp's?)
All those concepts thrown together without focus indeed make for a
confusing initial experience.

Several of the things that Yury mentions (how to safely close an event
loop) also don't belong in a tutorial. Honestly I'd just call
os._exit(0) when you are ready to exit, to avoid all the silly
cleanup-related errors that are trying to warn you against advanced
mistakes.

Glyph Lefkowitz

unread,
Oct 30, 2016, 6:54:34 PM10/30/16
to Donald Stufft, Guido van Rossum, Luciano Ramalho, Yury Selivanov, Justin Mayfield, python-tulip
Just as a data-point for this, I've been doing a few Twisted tutorials lately and we have taken a new tack which is to use Klein <https://github.com/twisted/klein> and Treq <https://github.com/twisted/treq> as real-world examples rather than restrict ourselves to what's in Twisted proper.  This has been a _huge_ success in terms of comprehensibility for newcomers; the concrete example that you can make a browser talk to is much easier to explain than the highly abstract "example" Deferreds that we have used in the past.  Some of the students at such tutorials would say stuff like "Actually I wanted to do something with IRC and IMAP, not a web app, but this gave me a much clearer idea of how to approach it."

So I think having asyncio's tutorial work this way would probably be a big win.

-glyph

Martin Richard

unread,
Oct 31, 2016, 8:10:36 AM10/31/16
to Glyph Lefkowitz, Donald Stufft, Guido van Rossum, Luciano Ramalho, Yury Selivanov, Justin Mayfield, python-tulip
Hi,

Like Justin, I'm happily using asyncio for 3 years now. After discussing with people during pycon-fr and a colleague who just started working on asyncio, I'd like to share a few thoughts I collected (and hope I won't make things more confusing).

I have the feeling that new users can not understand the "scope" of asyncio when reading the doc/api. Some see asyncio as a "new framework to do high performance concurrency" and some others as "the implementation in python of that new programming paradigm everybody talks about" (aka "python trying to compete against go").

Seeing asyncio as a low-level engine is unpleasant because you expect that porting your app to asyncio should be as easy as adding a couple of "async" and "await" keywords in front of blocking calls. It leads to "see how curio does it, it's way more convenient" or even worse: "I'd like to `$ pip install uvloop sanic` and get a faster flask app according to random-bench for free".

Seeing asyncio as a framework is confusing because you see the loop as the "app singleton", and start to assume things like:
- the loop keep tracks of "asyncio" objects: tasks, server instances, transport/protocol pairs,
- run_forever() acts as a main() which handle BaseExceptions (Ctrl+C) and program termination (it will shutdown servers, subprocesses, transport/protocols, ...),
- you should write and set an exception handler that makes sense to your app, ie: "what exception handler should I write so loop.call_exception_handler() prints the exception to the client of my HTTP app?"
- everything goes trough the loop, so you need an instance of the loop everywhere (intuition enforced by the loop keyword arg everywhere), and create everything through the loop instance (create_task, create_connection, etc).
- in this situation, you need get_event_loop() to return a context-aware result (= the loop running the current task), think that everything run inside a task (like on the system everything runs in a process/thread), and eventually you may want task-local storage and context (for instance, a resource or callback created in the task should be canceled when the task is canceled).
- the factory methods make you think that you should describe your program as a set of transport/protocols pairs that should be working together... then you get confused because you don't really use coroutines which seem to be a big thing in asyncio world.

Overall, I think that the doc is not the only issue, and that the asyncio ecosystem lacks of an actual framwork which enforces a structure for asyncio-based programs.
I would love to have the time (and experience, and ...) to write a library which fills those gaps:
- provide a few event loop policies (for multi-process or multi-thread apps),
- handle graceful termination of long-lived objects, manages task cancellation and exceptions, etc
- enforce the use of high-level objects only: streams rather than protocol/transports,
- discourage the use of futures in favor of synchronization primitives and coroutines (rather than functions returning Futures),
- hide the loop as much as possible, hence provide things like call_at() and call_later() as functions,
- provide some devtools abstracting the low-level parts (understandable "tracebacks" !) and making coroutine objects first class citizens (which I try with asynctest).

Cheers,
Martin
--
Martin Richard
www.martiusweb.net

wro...@riseup.net

unread,
Oct 31, 2016, 10:16:02 PM10/31/16
to gu...@python.org, Yury Selivanov, Justin Mayfield, python-tulip, gvanr...@gmail.com
On 2016-10-30 17:58, Guido van Rossum wrote:
> Regarding the documentation, I doubt we can get it in shape just by
> filing issues. We should really just have a tutorial written by
> someone with a good understanding of asyncio and writing skills.

From my experience, I had to understand how "single-threaded concurrent
code using coroutines" works. It is another concurrent programming model
and maybe description of how coroutines can be used for multitasking
(*without* asyncio) should be part of official Python tutorial after
multithreading section? [1]

Then asyncio documentation could simply refer to the tutorial and say
that
asyncio is using that model and is for "multiplexing I/O access over
sockets and other resources". Of course, it is also provider of
"infrastructure for writing single-threaded concurrent code using
coroutines", but...

Before asyncio, I was using coroutines for dataflow programming and
somehow never realized the "concurrent model" part. When I was starting
to use asyncio, I got an impression that it is required for coroutine
concurrent programming, which is not true. Once I separated these two
things in my head, it was easier to understand the need of event loop
to schedule asynchronous I/O tasks and event loop using futures to
represent these tasks... and the rest seems to be just an extension
to provide more functionality.

Above is quite personal perspective, but maybe it makes sense not only
for me? :)

Regards,

w

[1] To contrast concurrent execution of coroutines and threads,
beside Glyph's famous blog post, the "Nondeterminism of Threads"
frame at 2nd page of http://escholarship.org/uc/item/63b2c5cz could
be inspiring as well, IMHO.

Artur Wroblewski

unread,
Nov 1, 2016, 12:12:39 PM11/1/16
to gu...@python.org, gvanr...@gmail.com, Yury Selivanov, Justin Mayfield, python-tulip

On Nov 1, 2016 2:16 AM, wro...@riseup.net wrote:
>
> On 2016-10-30 17:58, Guido van Rossum wrote:
> > Regarding the documentation, I doubt we can get it in shape just by
> > filing issues. We should really just have a tutorial written by
> > someone with a good understanding of asyncio and writing skills.
>
> From my experience, I had to understand how "single-threaded concurrent
> code using coroutines" works. It is another concurrent programming model
> and maybe description of how coroutines can be used for multitasking
> (*without* asyncio) should be part of official Python tutorial after
> multithreading section? [1]
>

See also https://github.com/asyncio-doc/asyncio-doc/issues/15

Regards,

w

Reply all
Reply to author
Forward
0 new messages