Asynchronous reptile to write too much trouble? Trio to try it!

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/wufaliang003/article/details/91410645

Trio Trio is translated meaning, it provides a more convenient asynchronous programming, is asyncio more advanced packages.

It attempts to simplify complex asyncio module. To use a simple and Twisted than asyncio the same time, it has the same powerful features. The project is still young, still in the experimental stage but the overall design is reliable. Author encourage everyone to try to use, if you encounter a problem you can put him on the issue git. At the same time the author also provides an online chat room to communicate with its more convenient: https: //gitter.im/python-trio/general.

Ready to work

  • Make sure your Python version 3.5 and above.

  • Installation trio. python3 -m pip install --upgrade trio

  • import trio run if there is an error, no error may have been down.

Knowledge to prepare Async method

It means you need to use the trio has been writing asynchronous methods.

# 一个标准方法
def regular_double(x):
    return 2 * x

# 一个异步方法
async def async_double(x):
    return 2 * x

Visually asynchronous method and the standard method no difference just in front of more than a async.

"Async" is "asynchronous" shorthand, to distinguish it from an asynchronous function, we call standard functions for the synchronization function, with the following differences from the user point of view function asynchronous and synchronous functions:

  1. To call asynchronous function, you must use the await keyword. Therefore, do not write regular_double (3), but to write await async_double (3).

  2. You can not use the sync function in await, or an error occurs.
    Syntax error:

def print_double(x):
    print(await async_double(x))   # <-- SyntaxError here

However, in an asynchronous function, await is permitted:

async def print_double(x):
    print(await async_double(x))   # <-- OK!

To sum up: all the advantages as a user, asynchronous function relative to conventional functions is that asynchronous functions have super powers: they can call other asynchronous function.

In asynchronous asynchronous function you can call other functions, but things have beginnings and ends, the first asynchronous function how to call it?

Let's continue reading

How to call first asynchronous function

import trio

async def async_double(x):
    return 2 * x

trio.run(async_double, 3)  # returns 6

Here we can use to call trio.run first asynchronous function.
Let's look at the trio's other functions

The wait for asynchronous

import trio

async def double_sleep(x):
    await trio.sleep(2 * x)

trio.run(double_sleep, 3)  # does nothing for 6 seconds then returns

This uses asynchronous wait function trio.sleep, time.sleep (its functionality and synchronization functions of the) same, but because of the need to use await called, so the previous conclusion we know it is waiting for an asynchronous function method used.

The fact that this example of no practical use, we use the synchronization function can be achieved this simple function. Here it is mainly to demonstrate asynchronous function can be called by other asynchronous functions await.

The typical structure of an asynchronous function call

trio.run -> [async function] -> ... -> [async function] -> trio.whatever

Do not forget to write await

If you forget to write await what will happen, we look at the following example

import time
import trio

async def broken_double_sleep(x):
    print("*yawn* Going to sleep")
    start_time = time.perf_counter()

    # 糟糕,我忘了写await
    trio.sleep(2 * x)

    sleep_time = time.perf_counter() - start_time
    print("Woke up after {:.2f} seconds, feeling well rested!".format(sleep_time))

trio.run(broken_double_sleep, 3)

After running discovery

*yawn* Going to sleep
Woke up after 0.00 seconds, feeling well rested!
__main__:4: RuntimeWarning: coroutine 'sleep' was never awaited

Error, the error type is RuntimeWarning, followed by a sleep to say coroutine not use await.

We print the trio.sleep (3) see the following, this is represents a coroutine, an asynchronous function is clear from the foregoing contents.

We put above trio.sleep (2 * x) instead await trio.sleep (2 * x) can be.

If the runtime remember WARNING: coroutine 'RuntimeWarning: coroutine' ... 'was never awaited', it means that you do not have a place to write await.

Running multiple asynchronous function

If the trio await trio.sleep just use an example of such meaningless little value, so let's take a trio of other functions, to run multiple asynchronous function.

# tasks-intro.py

import trio

async def child1():
    print("  child1: started! sleeping now...")
    await trio.sleep(1)
    print("  child1: exiting!")

async def child2():
    print("  child2: started! sleeping now...")
    await trio.sleep(1)
    print("  child2: exiting!")

async def parent():
    print("parent: started!")
    async with trio.open_nursery() as nursery:
        print("parent: spawning child1...")
        nursery.start_soon(child1)

        print("parent: spawning child2...")
        nursery.start_soon(child2)

        print("parent: waiting for children to finish...")
        # -- we exit the nursery block here --
    print("parent: all done!")

trio.run(parent)

More content Let us analyze step by step, first defined and child2 two asynchronous functions, methods and definitions we said above, almost child1.

async def child1():
    print("child1: started! sleeping now...")
    await trio.sleep(1)
    print("child1: exiting!")

async def child2():
    print("child2: started! sleeping now...")
    await trio.sleep(1)
    print("child2: exiting!")

Next, we will define as a parent asynchronous function, it calls child1 and at the same time child2

async def parent():
    print("parent: started!")
    async with trio.open_nursery() as nursery:
        print("parent: spawning child1...")
        nursery.start_soon(child1)

        print("parent: spawning child2...")
        nursery.start_soon(child2)

        print("parent: waiting for children to finish...")
        # 到这里我们调用__aexit__,等待child1和child2运行完毕
    print("parent: all done!")

It is to create a "nursery" by using the mysterious async with the statement, and then child1 child2 added to the nursery by start_soon nusery methods.

Below us, say async with, is actually very simple, we know that when we use the file read with open () ... to create a file handle, with which involves two magic function

When a code block calls __enter started __ () call again at the end of __exit __ () we call open () is a context manager. async with someobj statements and asynchronous methods with similar except that it calls the magic function: __ aenter__ and __aexit__. We call someobj as "asynchronous context manager."

First, go back to the code above we use async with asynchronous create a block of code
at the same time by nursery.start_soon (child1) nursery.start_soon (child2) function call and start running child2 child1 and then return immediately, two asynchronous functions remain in the background continue to run.

Child1 and then wait for the end of child2 run, async with the end of the block in the content, print the final

"parent: all done!"。

Let's look at the results

parent: started!
parent: spawning child1...
parent: spawning child2...
parent: waiting for children to finish...
  child2: started! sleeping now...
  child1: started! sleeping now...
    [... 1 second passes ...]
  child1: exiting!
  child2: exiting!
parent: all done!

And it can be found on our analysis of the same. See here, if you are familiar with thread, you will find this similar operation mechanism and multithreading. But there is not a thread, where all the code in a thread inside the complete, in order to distinguish thread here and we call child1 child2 two tasks, with the task, we can only be certain that we call "checkpoints" of designated place to switch. We'll dig deep behind it.

trio in the tracker

We know that multiple tasks are carried out above switching operation in one thread, but for we do not know how to switch, and only those of us who know better can learn a module.
Fortunately, trio provides a set of tools for checking and debugging. We can write a Tracer class to achieve trio.abc.Instrumen interface. code show as below

class Tracer(trio.abc.Instrument):
    def before_run(self):
        print("!!! run started")

    def _print_with_task(self, msg, task):
        # repr(task) is perhaps more useful than task.name in general,
        # but in context of a tutorial the extra noise is unhelpful.
        print("{}: {}".format(msg, task.name))

    def task_spawned(self, task):
        self._print_with_task("### new task spawned", task)

    def task_scheduled(self, task):
        self._print_with_task("### task scheduled", task)

    def before_task_step(self, task):
        self._print_with_task(">>> about to run one step of task", task)

    def after_task_step(self, task):
        self._print_with_task("<<< task step finished", task)

    def task_exited(self, task):
        self._print_with_task("### task exited", task)

    def before_io_wait(self, timeout):
        if timeout:
            print("### waiting for I/O for up to {} seconds".format(timeout))
        else:
            print("### doing a quick check for I/O")
        self._sleep_time = trio.current_time()

    def after_io_wait(self, timeout):
        duration = trio.current_time() - self._sleep_time
        print("### finished I/O check (took {} seconds)".format(duration))

    def after_run(self):
        print("!!! run finished")

Then we run the previous example, but this time we passed a Tracer object.

trio.run(parent, instruments=[Tracer()])

Then we will find a part of our print part of a lot of things the following analysis.

!!! run started
### new task spawned: <init>
### task scheduled: <init>
### doing a quick check for I/O
### finished I/O check (took 1.787799919839017e-05 seconds)
>>> about to run one step of task: <init>
### new task spawned: __main__.parent
### task scheduled: __main__.parent
### new task spawned: <TrioToken.run_sync_soon task>
### task scheduled: <TrioToken.run_sync_soon task>
<<< task step finished: <init>
### doing a quick check for I/O
### finished I/O check (took 1.704399983282201e-05 seconds)

In front of a lot of information that we do not have to care, we see ### new task spawned:. __Main __ parent, seen __main __ parent created a task.

Once the initial management work is completed, trio began to run parent function, you can see the parent function creates two sub-tasks. Then, it reaches the end asynchronous block form, and pause.

>>> about to run one step of task: __main__.parent
parent: started!
parent: spawning child1...
### new task spawned: __main__.child1
### task scheduled: __main__.child1
parent: spawning child2...
### new task spawned: __main__.child2
### task scheduled: __main__.child2
parent: waiting for children to finish...
<<< task step finished: __main__.parent

Then to the trio.run (), recorded more inner workings of the process.

>>> about to run one step of task: <call soon task>
<<< task step finished: <call soon task>
### doing a quick check for I/O
### finished I/O check (took 5.476875230669975e-06 seconds)

Then give a chance to these two sub-tasks running

>>> about to run one step of task: __main__.child2
  child2 started! sleeping now...
<<< task step finished: __main__.child2

>>> about to run one step of task: __main__.child1
  child1: started! sleeping now...
<<< task step finished: __main__.child1

Each task is running, until the call trio.sleep () and then suddenly we're back trio.run () decide what you want to run. How is this going? The secret lies in trio.run () and trio.sleep () to achieve together, trio.sleep () can get some special magic, it suspended the entire call stack, so it sends a notification to the trio.run (), request wake up again after one second, then pause task. After the task pause, Python will control back trio.run (), by its decision what to do next.

Note: You can not use asyncio.sleep () in the trio.

Then it calls a primitive operating system to make the whole process to sleep

### waiting for I/O for up to 0.9997810370005027 seconds

After 1s sleep

### finished I/O check (took 1.0006483688484877 seconds)
### task scheduled: __main__.child1
### task scheduled: __main__.child2

Remember how the parent is waiting for the end of the two sub-tasks, observe the following when parent child1 quit doing

>>> about to run one step of task: __main__.child1
  child1: exiting!
### task scheduled: __main__.parent
### task exited: __main__.child1
<<< task step finished: __main__.child1

>>> about to run one step of task: __main__.child2
  child2 exiting!
### task exited: __main__.child2
<<< task step finished: __main__.child2

Then to be io operation, and then the end of the parent task

### doing a quick check for I/O
### finished I/O check (took 9.045004844665527e-06 seconds)

>>> about to run one step of task: __main__.parent
parent: all done!
### task scheduled: <init>
### task exited: __main__.parent
<<< task step finished: __main__.parent

Finally, the end of some internal operation code

### doing a quick check for I/O
### finished I/O check (took 5.996786057949066e-06 seconds)
>>> about to run one step of task: <init>
### task scheduled: <call soon task>
### task scheduled: <init>
<<< task step finished: <init>
### doing a quick check for I/O
### finished I/O check (took 6.258022040128708e-06 seconds)
>>> about to run one step of task: <call soon task>
### task exited: <call soon task>
<<< task step finished: <call soon task>
>>> about to run one step of task: <init>
### task exited: <init>
<<< task step finished: <init>
!!! run finished

ok, just say this part of the operation mechanism can be understood, of course, remember the more convenient understanding of the trio.
More about the trio of use, so stay tuned. . .

No micro-channel public attention and headlines today, excellent article continued update. . . . .

 

 

Guess you like

Origin blog.csdn.net/wufaliang003/article/details/91410645