On Sun, Jun 14, 2020 at 10:38 PM J. Pic email@example.com wrote:
Well correct me if I'm wrong (I did a bit of homework), but threads are hard to get right because they may switch at any time.
When we do async instead of threads, it's because we want task switch on blocking operations only.
That's an oversimplification, but yes. If you have code like this:
limit = 10 def spam(some,stuff): global limit if limit: limit -= 1 spam(more,stuff)
then yes, you are extremely at risk of multiple threads messing you up, because between "if limit" and "limit -= 1", another thread could run the same code. Doing the same thing with an async function and "await spam(more,stuff)" wouldn't have this risk, because you know for sure that there can be no context switch outside of that "await" keyword.
But the problem here isn't threads. The problem is mutable globals, and not having any sort of lock to keep it safe. Threads aren't inherently dangerous in the sense that everything will suddenly break the instant you create a second thread; what happens is that vulnerable code can work in a single-threaded situation without ever being triggered.
So async/await is easier to get right because it behaves like single-threaded code but with well-defined context switches, but multi-threaded code isn't THAT hard to get right.
Both async/await and Python threads are vulnerable to the problem that a *spinning* thread will prevent other tasks from running, but with async/await, ANY big job will block, whereas with threads, it has to be something keeping the CPU busy while holding the GIL. So threads are safer against that sort of thing, while being vulnerable to a different sort of thing (a thing which, IMO, is a coding error anyway).