I. Concepts of the protocol
Protocol, also known as micro-threading, fiber.The English name is Coroutine.
Collaboration is another way of multitasking in python, but takes up less execution units (understood as required resources) than threads.
The general understanding is that a function in a thread can store information about some temporary variables of the current function anywhere, and then switch to another function to execute. Note that this is not done by calling a function, and the number of times and when to switch back to the original function are determined by the developer himself.
When implementing multitasking, thread switching is much simpler at the system level than saving and restoring CPU contexts.Operating system For the efficiency of running programs, each thread has its own cache of data such as Cache, and the operating system also helps you recover this data.So switching threads can be very performance intensive.But protocol switching is simply the context of operating the CPU, so millions of system switches per second are resistant.
2. Ways of realizing the agreement
2.1 via yield
# -*- coding:utf-8 -*-
import time
def work1():
while True:
print('--work1--')
yield
time.sleep(0.5)
def work2():
while True:
print('-work2')
yield
time.sleep(0.5)
def main():
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
if __name__ == '__main__':
main()
//Result:
--work1--
-work2
--work1--
-work2
--work1--
-work2
--work1--
-work2
--work1--
-work2
--work1--
-work2
......
You can see that Task 1 and Task 2 are executed alternately to achieve multitask.
2.2 via greenlet
In order to better use collaborations to accomplish multitasks, the greenlet module in python encapsulates them, making it easier to switch tasks.
Install the greenlet module using the following command:
sudo pip3 install greenlet
# -*- coding:utf-8 -*-
import time, random, greenlet
def test1(num):
while True:
print('A')
print(num)
gr2.switch(2)
time.sleep(random.random())
def test2(num):
while True:
print('B')
print(num)
gr1.switch(1)
time.sleep(random.random())
gr1 = greenlet.greenlet(test1)
gr2 = greenlet.greenlet(test2)
gr1.switch(1)
//Result:
A
1
B
2
A
1
B
2
A
1
......
You can see that multitasking is also implemented, but you just need to switch tasks manually.
2.3 via gevent
greenlet has implemented the protocol, but manual switching is required, and python has a more powerful module gevent than greenlet that can automatically switch tasks.
The principle is that when a greenlet encounters IO (i.e., input output input and output, such as network, file operations, etc.) operations, such as accessing the network, it automatically switches to another greenlet until the IO operation is complete, and then switches back to continue execution at the appropriate time.
Because IO operations are time consuming and often leave programs waiting, with gevent switching the protocol for us automatically, there is always a greenlet running, not waiting for IO.
# -*- coding:utf-8 -*-
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
# To simulate a time-consuming operation, note that it is not sleep in the time module
gevent.sleep(1)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
#Change the number of loops to 500,000 to make them run a little longer, and then in the process manager of the operating system, there is only one thread.
# g1 = gevent.spawn(f, 500000)
# g2 = gevent.spawn(f, 500000)
# g3 = gevent.spawn(f, 500000)
g1.join()
g2.join()
g3.join()
//Result:
<Greenlet "Greenlet-0" at 0x10b8f0a48: f(5)> 0
<Greenlet "Greenlet-1" at 0x10b8f0d48: f(5)> 0
<Greenlet "Greenlet-2" at 0x10b8f0e48: f(5)> 0
<Greenlet "Greenlet-0" at 0x10b8f0a48: f(5)> 1
<Greenlet "Greenlet-1" at 0x10b8f0d48: f(5)> 1
<Greenlet "Greenlet-2" at 0x10b8f0e48: f(5)> 1
<Greenlet "Greenlet-0" at 0x10b8f0a48: f(5)> 2
<Greenlet "Greenlet-2" at 0x10b8f0e48: f(5)> 2
<Greenlet "Greenlet-1" at 0x10b8f0d48: f(5)> 2
<Greenlet "Greenlet-0" at 0x10b8f0a48: f(5)> 3
<Greenlet "Greenlet-1" at 0x10b8f0d48: f(5)> 3
<Greenlet "Greenlet-2" at 0x10b8f0e48: f(5)> 3
<Greenlet "Greenlet-0" at 0x10b8f0a48: f(5)> 4
<Greenlet "Greenlet-2" at 0x10b8f0e48: f(5)> 4
<Greenlet "Greenlet-1" at 0x10b8f0d48: f(5)> 4
# -*- coding:utf-8 -*-
from gevent import monkey; #monkey.patch_all() This method needs to be placed first or there will be a warning
import gevent, random, time
#Patch: when time-consuming operations are required, automatically detect time-consuming operations and automatically convert time.sleep(random.random()) to gevent.sleep(random.random())
# monkey.patch_all() # Replace code for time-consuming operations used in the program with modules implemented by yourself in gevent
def coroutine_work(coroutine_name):
for i in range(10):
print(coroutine_name, i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work, 'work1'),
gevent.spawn(coroutine_work, 'work2')
])
//Result:
work1 0
work1 1
work1 2
work1 3
work1 4
work1 5
work1 6
work1 7
work1 8
work1 9
work2 0
work2 1
work2 2
work2 3
work2 4
work2 5
work2 6
work2 7
work2 8
work2 9
3. Comparison of processes, threads and collaborations
- A process is the unit of resource allocation for the operating system.
- Threads are units of CPU scheduling;
- Process switching requires the most resources and is inefficient.
- Thread switching requires average resources and efficiency (regardless of GIL, of course);
- The resource of the cooperative switching task is small and the efficiency is high.
- Multiprocessing and multithreading may be parallel depending on the number of cpu cores, but the collaboration is in one thread, so it is concurrent.