yield
Since we want to talk about coroutines, we have to mention the usage of yield first. There are two main usages of yield. yield r and n = yield r ,
A brief understanding of yield: yield means that return returns a value, and remembers the position of this return, the next iteration will start from this position.
def foo():
print("starting...")
while True:
res = yield 4
print("res:", res)
g = foo()
print(next(g))
print("-" * 20)
print(next(g))
starting...
4
--------------------
res: None
4
n = yield r 和 r = c.send(n)
def foo():
print("starting...")
while True:
res = yield 4
print("res:", res)
g = foo()
print(next(g))
print("-" * 20)
# print(next(g))
print(g.send(7))
starting...
4
--------------------
res: 7
4
It is not difficult to understand from the above example, next(g) and g.send(param) are actually relatively similar, but the latter one passes the parameters in the next step, or you can also think that next(g) is also The parameters are passed in, but None is passed.
So, this can also explain why next(g) can also be replaced with g.send(None).
If you have any questions, you can take a look at this blog: https://blog.csdn.net/mieleizhi0522/article/details/82142856/ , I believe that after reading this blog, you can look at the code in the following example. It's struggling.
Coroutine
The biggest advantage of coroutine is its extremely high execution efficiency. Because subroutine switching is not thread switching, it is controlled by the program itself . Therefore, there is no thread switching overhead. Compared with multithreading, the more threads, the more obvious the performance advantage of coroutines.
The second major advantage is that there is no need for a multi-threaded lock mechanism, because there is only one thread, and there is no conflict of writing variables at the same time. In the coroutine, the shared resources are controlled without locking, and only the state is judged, so the execution efficiency is much higher. The thread is much higher.
Generally speaking, when talking about coroutines, I like to use the following classic producer consumer example: execution result:
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK
Operation process:
Note that the consumer
function is one generator
, after consumer
passing produce
in one:
-
First call the
c.send(None)
startup generator (the utility next(c) startup is also possible); -
Then, once something is produced,
c.send(n)
switch toconsumer
execution by; -
consumer
Byyield
getting the message, processing it, and passingyield
the result back; -
produce
Get theconsumer
processed result and continue to produce the next message; -
produce
It was decided not to produce, and the whole process ended byc.close()
shutting downconsumer
.
The entire process is lock-free, executed by one thread, produce
and consumer
cooperated to complete the task, so it is called "coroutine", rather than thread preemptive multitasking.