> If
you're fine with invisible context switches, you're probably better
off with threads, because they're not vulnerable to unexpectedly
blocking actions (a common culprit being name lookups before network
transactions - you can connect sockets asynchronously, but
gethostbyname will block the current thread).

These "unexpectedly blocking actions" can be identified in asyncio's debug mode. Specifically, any callback or task step that has a duration greater than 100ms will be logged. Then, the user can take a closer look at the offending long running step. If it's like socket.gethostbyname() and is a blocking IO-bound function call, it can be executed in a thread pool using loop.run_in_executor(None, socket.gethostbyname, hostname) to avoid blocking the event loop. In 3.9, there's also a roughly equivalent higher-level function that doesn't require access to the event loop: asyncio.to_thread(socket.gethostbyname, hostname).

With the default duration of 100ms, it likely wouldn't pick up on socket.gethostbyname(), but it can rather easily be adjusted via the modifiable loop.slow_callback_duration attribute.

Here's a quick, trivial example:
```
import asyncio
import socket

async def main():
    loop = asyncio.get_running_loop()
    loop.slow_callback_duration = .01 # 10ms
    socket.gethostbyname("python.org")

asyncio.run(main(), debug=True)
# If asyncio.run() is not an option, it can also be enabled via:
#     loop.set_debug()
#     using -X dev
#     PYTHONASYNCIODEBUG env var
```
Output (3.8.3):
Executing <Task finished name='Task-1' coro=<main() done, defined at asyncio_debug_ex.py:5> result=None created at /usr/lib/python3.8/asyncio/base_events.py:595> took 0.039 seconds

This is a bit more involved than it is for working with threads; I just wanted to demonstrate one method of addressing the problem, as it's a decently common issue. For more details about asyncio's debug mode, see https://docs.python.org/3/library/asyncio-dev.html#debug-mode.


On Sat, Jun 13, 2020 at 9:44 PM Chris Angelico <rosuav@gmail.com> wrote:
On Sun, Jun 14, 2020 at 11:29 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
>
> On 14/06/20 12:05 pm, Chris Angelico wrote:
> > On Sun, Jun 14, 2020 at 9:54 AM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
> >> Likewise, it's legitimate to create an awaitable
> >> object and then await it later.
> >>
> >> (Personally I think it *shouldn't* be legitimate to do that in
> >> the case of await, but Guido thinks otherwise, so it is the way
> >> it is.)
> >
> > If it isn't, then how do you start multiple tasks in parallel?
>
> There would need to be a primitive that takes an async def function
> and creates an awaitable from it. The API for spawning tasks would
> then take an async function and use this primitive to get things
> rolling. So it wouldn't be impossible to separate the two, but you
> would have to go out of your way to do it. It wouldn't be the usual
> way to do things.
>
> (For more on this, look up the discussions about my "cofunctions"
> idea. It was very similar to async/await, except that the operations
> of calling an async function and awaiting the result were fused into
> a single syntactic entity.)
>

Hmm, I think I see what you mean. So awaiting it would be "spam(123)"
and getting an awaitable for later would be "spam.defer(123)"? That
would make reasonable sense.

Still, I don't think it's of value, since part of the point of
coroutines is knowing exactly where a context switch could happen. If
you're fine with invisible context switches, you're probably better
off with threads, because they're not vulnerable to unexpectedly
blocking actions (a common culprit being name lookups before network
transactions - you can connect sockets asynchronously, but
gethostbyname will block the current thread).

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WDYCTCHG6NH36W5QS46NYEBOQVGUPOSR/
Code of Conduct: http://python.org/psf/codeofconduct/