Live data from Hacker News

Python and Async Simplified (2018)

aeracode.org

31–40 of 47 posts

Re: Python and Async Simplified (2018)

#31
post #26
post #20

Earlier quoted context omitted.

since it's my job to clear these things up, a few pointers: 1. python has threads. they just cannot perform CPU bound tasks in parallel due to the GIL. The GIL is released for IO, so threads can perform IO waiting in parallel, just like asyncio 2. asyncio runs in one thread, and has the exact same limitations as threads as implemented in Python, CPU operations are serialized, async tasks can yield for IO. the advanta…

I've been coding Python since 2.5 days and I have yet to have a use case where I've really needed asyncio. For client-side code, concurrent.futures (specifically ThreadPoolExecutor) has satisfied nearly every use case, though occasionally I'll use a a worker-thread model. For server-side code, I'd still probably use threads up to maybe 1000 concurrent connections. Beyond that, I've used gevent to good effect. e.g., I…

For me it's not about efficiency. Using asyncio is just easier than threads.

* One coroutine can only interrupt another one at a point clearly marked with await (or async for or async with). That makes it easier to avoid data races without explicit synchronisation like locks.

* It's much easier to spawn async tasks and avoid them getting lost than with threads, assuming you use asyncio task groups (either by using a future version of Python, or using the anyio library now, or using Trio instead of asyncio).

* Async operations all have first class support for cancellation, and this interacts really cleanly with task groups. That helps with things like time outs, clean shutdown of your program, or cleaning up all resources related to a connection when that connection is closed.

* There's a bit more boilerplate in spawning threads and exchanging messages with them and joining them than the is spawning async tasks, especially when using task groups. (Admittedly, this is a solvable problem, and there are probably good libraries out there to help with this.)

Re: Python and Async Simplified (2018)

#32

I am really interested in this space. There's an article that Cal Paterson wrote that async doesn't speed up code - it is not parallel. The GIL prevents Python from being parallel. So even if you create a thread to run an async method in Python, it shall not run in parallel to the main thread of execution. (In fact, it shall block the main thread of execution if you start a thread in the thread you are in, due to the…

> await result1;

> await result2;

> await result3;

Not really, you only have *1* request in flight.

And you're waiting for them sequentially.

You need asyncio.gather ( https://docs.python.org/3/library/asyncio-task.html#asyncio.... ) if you want to run tasks concurrently.

results = await asyncio.gather(result1, result2, result3)

Re: Python and Async Simplified (2018)

#33

It gives good pointers but it falls short on the usual suspects for an article on asyncio. When teaching it, it's important to emphasis: - await is locally blocking, so you should isolate linear workflows into their own coro, which is the unit of concurrency. - to allow concurrency, you should use asyncio.create_task on coro (formerly ensure_future). - you should always explicitly delimitate the life cycle of any tas…

> to allow concurrency, you should use asyncio.create_task on coro (formerly ensure_future). This is misleading... you can use asyncio.gather which does this internally [0]. [0]: https://github.com/python/cpython/blob/main/Lib/asyncio/task...

Only if you wish to collect tasks where you schedule them, accumulate the results and don't need to limit concurrency.

Re: Python and Async Simplified (2018)

#34
post #8

Async is overengineered and bolted on. If you must use Python, I'd still recommend Twisted, which is more accessible. Otherwise, of course use Go, Elixir, etc. in the first place.

I find Twisted less accessible and more opaque than async. It's also more "bolted on" in that it's an entirely separate library/framework outside the standard lib.

Async might technically be bolted on, but no worse than async in most languages which weren't designed de novo for async (eg go/elixir).

Re: Python and Async Simplified (2018)

#35

I am really interested in this space. There's an article that Cal Paterson wrote that async doesn't speed up code - it is not parallel. The GIL prevents Python from being parallel. So even if you create a thread to run an async method in Python, it shall not run in parallel to the main thread of execution. (In fact, it shall block the main thread of execution if you start a thread in the thread you are in, due to the…

> await result1; > await result2; > await result3; Not really, you only have *1* request in flight. And you're waiting for them sequentially. You need asyncio.gather ( https://docs.python.org/3/library/asyncio-task.html#asyncio.... ) if you want to run tasks concurrently. results = await asyncio.gather(result1, result2, result3)

Before the first await result1 the coroutine objects are in flight.

Re: Python and Async Simplified (2018)

#36
post #24
post #4

Similar question to the other one at the time of writing, but more specific: does anyone have a good, thorough introduction to the "async event loop" (sometimes known as "asyncio") pattern? By thorough I mean that it goes beyond a starter tutorial, into both examples of various supporting libraries and implementation details that matter for usage. I'm fine with a book, too. There are popular libraries for it in both…

I found this post to be amazing intro that shows you how to go from simple generators to async event loop. https://mleue.com/posts/yield-to-async-await/

This was very good! But true to what's common, it stops just as it gets really juicy!

Re: Python and Async Simplified (2018)

#37
post #26
post #20

Earlier quoted context omitted.

since it's my job to clear these things up, a few pointers: 1. python has threads. they just cannot perform CPU bound tasks in parallel due to the GIL. The GIL is released for IO, so threads can perform IO waiting in parallel, just like asyncio 2. asyncio runs in one thread, and has the exact same limitations as threads as implemented in Python, CPU operations are serialized, async tasks can yield for IO. the advanta…

I've been coding Python since 2.5 days and I have yet to have a use case where I've really needed asyncio. For client-side code, concurrent.futures (specifically ThreadPoolExecutor) has satisfied nearly every use case, though occasionally I'll use a a worker-thread model. For server-side code, I'd still probably use threads up to maybe 1000 concurrent connections. Beyond that, I've used gevent to good effect. e.g., I…

in my largely non-scientific experience, Python starts to fall over at about 50 threads, 1000 seems impossible but I haven't really tried.

Re: Python and Async Simplified (2018)

#38
post #26

Earlier quoted context omitted.

I've been coding Python since 2.5 days and I have yet to have a use case where I've really needed asyncio. For client-side code, concurrent.futures (specifically ThreadPoolExecutor) has satisfied nearly every use case, though occasionally I'll use a a worker-thread model. For server-side code, I'd still probably use threads up to maybe 1000 concurrent connections. Beyond that, I've used gevent to good effect. e.g., I…

For me it's not about efficiency. Using asyncio is just easier than threads. * One coroutine can only interrupt another one at a point clearly marked with await (or async for or async with). That makes it easier to avoid data races without explicit synchronisation like locks. * It's much easier to spawn async tasks and avoid them getting lost than with threads, assuming you use asyncio task groups (either by using a…

well that's the thing with threads, you shouldn't be "spinning them up on the fly", you should have a fixed pool of threads. That then involves some architectural work up front (like 5 lines of code, ugh) and that's where everyone (under age 40) yawns and goes off to use asyncio instead (which oddly enough has a worker thread running in the form of the event loop, it's just all been presented nicely).

Re: Python and Async Simplified (2018)

#39
post #38

Earlier quoted context omitted.

For me it's not about efficiency. Using asyncio is just easier than threads. * One coroutine can only interrupt another one at a point clearly marked with await (or async for or async with). That makes it easier to avoid data races without explicit synchronisation like locks. * It's much easier to spawn async tasks and avoid them getting lost than with threads, assuming you use asyncio task groups (either by using a…

well that's the thing with threads, you shouldn't be "spinning them up on the fly", you should have a fixed pool of threads. That then involves some architectural work up front (like 5 lines of code, ugh) and that's where everyone (under age 40) yawns and goes off to use asyncio instead (which oddly enough has a worker thread running in the form of the event loop, it's just all been presented nicely).

It sounds like you're talking about a different situation. The parent comment was taking about thread-per-connection with blocking IO read call on each. Yes that means spinning up and shutting down threads as connections open and close, and that is 100% a valid strategy. If you have a fixed pool of n threads and you get n+1 connections then you're just going to have to ignore one at any given time (potentially causing deadlock depending on the relationship between the connections) or end up using a multiplexing API at which point you're not far off from async world anyway.

Maybe you're talking about just submitting independent work items to run concurrently – yes async won't help much with that, because you're in the most trivial situation possible.

In more complex situations, with interrelationships between tasks (/threads), async syntax and task groups definitely has a huge impact. And, as I said, that's before you even get into how much easier it makes cancellation.

Re: Python and Async Simplified (2018)

#40
post #37
post #26

Earlier quoted context omitted.

I've been coding Python since 2.5 days and I have yet to have a use case where I've really needed asyncio. For client-side code, concurrent.futures (specifically ThreadPoolExecutor) has satisfied nearly every use case, though occasionally I'll use a a worker-thread model. For server-side code, I'd still probably use threads up to maybe 1000 concurrent connections. Beyond that, I've used gevent to good effect. e.g., I…

in my largely non-scientific experience, Python starts to fall over at about 50 threads, 1000 seems impossible but I haven't really tried.

1000 threads was a very specific use case I probably shouldn't have generalized from where I needed to match the number of Python threads running in a web server to a Java process running on the same host using the same number of threads. They were mostly idle.

There's no reason Python should fall over at any number of threads. You just usually end up either running out of memory or (more likely) saturate a single CPU core well before that number of threads.

Without consulting my notes I can't recall why I didn't use gevent on that project.

Post reply on HN