3 ms·
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 do
by ogogmad 4y ago
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?
- diarrhea 4y agoI 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-guide/concepts/async/task-asynchronous-programming-model#BKMK_Threads https://learn.microsoft.com/en-us/dotnet/csharp/programming-... ("The async and await keywords don't cause additional threads to be created.")
- tomnipotent 4y agoEliminates 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.
- zzzeek 4y agosince 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 advantages offered by asyncio are: 1. you can have thousands of tasks extremely quickly cheaply, which is not as much the case for threads in Python . this can allow for massive concurrent architectures more expediently, provided your concurrency is very IO bound (if you are CPU bound, disaster) 2. people just like asyncio's programming model, IMPO this is largely due to the popularity of Javascript's event-based model being natural for lots of newer programmers
- morbia 4y agoThe GIL gets even more complicated than that because it can also be released during CPU bound tasks that don't interact with python objects (e.g. Array operations in numpy)
- zzzeek 4y agoRight, if your cpu tasks are running in native extensions, then threading will actually allow parallelism, whereas asyncio will not.
- js2 4y agoI'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 have a server that receives HTTP POSTs which are multipart forms, the form having 3 parts, a JSON part and two file parts. The two files parts get written to files on S3 and the JSON part to SQS. The web framework is Falcon[1] and I also made use of a Cython-based HTTP form parser[2]. Concurrency is handled via gevent. Openresty sits in front and invokes the Python server via uwsgi. At the time I developed it, asyncio was not yet mature and not supported by boto3. I benchmarked against pypy but unsurprisingly (since it's I/O bound) got better performance and from CPython + gevent. If I were developing it from scratch today, I'd re-evaluate the asyncio story, or more likely than not, choose a different language. I don't doubt that there's use-cases to which asyncio is well-suited and the right choice, but I suspect folks may be using it in cases where they'd be fine with threads. As always, there are trade-offs. 1. https://falconframework.org/ https://falconframework.org/ 2. https://pypi.org/project/streaming-form-data/ https://pypi.org/project/streaming-form-data/ (I think)
- quietbritishjim 4y agoFor 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.)
- throwway2332 4y ago> I thought Python couldn't multithread because of GIL? Why would it need to in this case? You only need one thread for concurrent I/O.