Live data from Hacker News

Python and Async Simplified (2018)

aeracode.org

41–47 of 47 posts

Re: Python and Async Simplified (2018)

#41
post #38

Earlier quoted context omitted.

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 causin…

yes, parent was referring to "each thread with a blocking connection", but you still can (and probably should) use a thread pool for that. In the naive approach, new connections beyond the limit of your threadpool either have to wait, or you have to dynamically expand your threadpool. mod_wsgi's daemon mode has the option to use a thread pool of a fixed size to handle requests.

you can also use non-blocking handles with a fixed /dynamic threadpool and use epoll or similar to find those handles with data ready, and send those into your pool, thereby servicing an arbitrary number of connections with a controlled level of concurrency among them. MariaDB has an option to do that here: https://mariadb.com/kb/en/thread-pool-in-mariadb/ . this is not as trivial as spinning up asyncio tasks but that's because there's (AFAIK) no friendly library giving you an easy way of doing it. But it's Python and if you're writing a server to handle MariaDB server loads using a thread pool with direct use of epoll(), you're likely in the wrong language.

Re: Python and Async Simplified (2018)

#42
post #12

I thought Python couldn't multithread because of GIL? I understood from the article that async derives all its benefit from certain OS-level operations which don't need to run in a coroutine, like reading from a network socket or waiting for timers to finish. Another question: Is Python's implementation of async/await identical to other languages? In particular, do they always use coroutines instead of threads?

I understand async/await in Python to be entirely single-threaded. So is, for example, C#'s implementation: https://learn.microsoft.com/en-us/dotnet/csharp/programming-... ("The async and await keywords don't cause additional threads to be created.")

Eliminates blocking on IO requests, letting the event loop spend CPU cycles doing non-IO work. Alternative is CPU doing nothing while waiting on IO, which for something like a web app doing lots of small network requests to database/cache can add up to a lot. CPU work is still single-threaded.

Re: Python and Async Simplified (2018)

#43

This is the bit that should be at the very top of the official docs. It's tripped me up every time I go to write async code and until you learn it, the error message is very confusing. > In particular, calling it will immediately return a coroutine object, which basically says "I can run the coroutine with the arguments you called with and return a result when you await me". > The code in the target function isn't ca…

> If I try to pass the async function to gather (for example) without calling it, which makes some intuitive sense

That intuition breaks immediately when you realize that those functions can have arguments, and you have no way to pass them.

Re: Python and Async Simplified (2018)

#44

Earlier quoted context omitted.

> 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.

Nope, creating the coroutines doesn’t schedule them for execution. That only happens on await. Python is not eager. If you want that behavior you need to use create_task. It doesn’t work like spawning a thread and waiting on them.

From the docs: https://docs.python.org/3/library/asyncio-task.html

> Note that simply calling a coroutine will not schedule it to be executed:

Re: Python and Async Simplified (2018)

#45

Earlier quoted context omitted.

> 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.

Serious question, why would you limit concurrency in async world? You’re still on a single thread, why would you want to only schedule n things at a time?

Re: Python and Async Simplified (2018)

#46
post #10

Earlier quoted context omitted.

> you should always explicitly delimitate the life cycle of any task Unless you want a hacky actor system, in which case it's totally fine to `create_task` a ton of corountines which have their own spin loop with await sleep :)

One coroutine crashing and the others continuing to send it messages without noticing was my $40,000 bug.

I’m so confused by this architecture. It makes total sense in a threaded world but why would you want a coroutine constantly scheduling itself in a loop to pull messages off a queue like thing than just having the thing generating the message fire off a task to process it directly right there? It feels almost the same to me and then you can’t crash the coroutine.

Re: Python and Async Simplified (2018)

#47
post #44

Earlier quoted context omitted.

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

Nope, creating the coroutines doesn’t schedule them for execution. That only happens on await. Python is not eager. If you want that behavior you need to use create_task. It doesn’t work like spawning a thread and waiting on them. From the docs: https://docs.python.org/3/library/asyncio-task.html > Note that simply calling a coroutine will not schedule it to be executed:

Oh this is not what I expected.

I think on C# you can await threads which is similar to a join() with a return value.

Post reply on HN