1.协程
并发:切+保存状态
单线程下实现并发:协程 切+ 保存状态 yield
遇到io切,提高效率
遇到计算切,并没有提高效率
检测单线程下 IO行为 io阻塞 切
相当于骗操作系统 一直处于计算
协程:。。。
单线程下实现并发:根本目标:遇到IO就切,一个线程的整体IO降下来
程序用的cpu 时间长,就叫执行效率高
效率最高:多个进程 (多个cpu)
每个进程开多个线程
每个线程用到协程 (IO就切)
总结协程特点:
1 #并发执行
2 import time
3
4 def producer():
5 g=consumer()
6 next(g)
7 for i in range(10000000): # 计算
8 g.send(i)
9
10
11 def consumer():
12 while True:
13 res=yield
14
15
16 start_time=time.time()
17 producer()
18 stop_time=time.time()
19 print(stop_time-start_time)
20
21 #串行
22 import time
23
24 def producer():
25 res=[]
26 for i in range(10000000):
27 res.append(i)
28 return res
29
30
31 def consumer(res):
32 pass
33
34
35 start_time=time.time()
36 res=producer()
37 consumer(res)
38 stop_time=time.time()
39 print(stop_time-start_time)
2.greenlet模块
pip3 install greenlet
greenlet:可以很方便的切 但不能检测到 遇到IO 切
greenlet 比yield 好 但是还是不好 遇到io不会切
1 from greenlet import greenlet
2 import time
3
4 def eat(name):
5 print('%s eat 1' %name)
6 time.sleep(10) # 遇到io 不会立即切
7 g2.switch('egon')
8 print('%s eat 2' %name)
9 g2.switch()
10
11 def play(name):
12 print('%s play 1' %name )
13 g1.switch()
14 print('%s play 2' %name )
15
16
17 g1=greenlet(eat)
18 g2=greenlet(play)
19
20 g1.switch('egon') # 第一次切 需要传参数
3.gevent模块
pip3 install gevent
gevent:封装了greenlet模块,但是他能检测到io 自动切
只能检测到gevent.sleep() gevent的IO阻塞
加上补丁后,就可以检测到所有的IO 原理是:将阻塞变为非阻塞
from gevent import monkey;monkey.patch_all()
这种形式的协程 才能帮我们提升效率 从始至终 就一个线程
gevent.joinall([g1,g2]) 等待全部执行完
gevent 模块:监测单线程下多个任务得IO行为实现遇到IO就自动切到另一个任务去执行
提升单线程运行效率
应用场景:单线程下多个任务io密集型
ftp io密集型 线程来回切 比os q切 小路高
1 from gevent import monkey;monkey.patch_all() # 一定要放在程序的开头 检测所以的io 将阻塞变成非阻塞
2 import gevent
3 import time
4
5
6 def eat(name):
7 print('%s eat 1' % name)
8 time.sleep(3) # 7s多一些 gevent 只识别 gevent 的 io操作
9 # gevent.sleep(3) # 4s 多一些
10 print('%s eat 2' % name)
11
12
13 def play(name):
14 print('%s play 1' % name)
15 time.sleep(4)
16 # gevent.sleep(4)
17 print('%s play 2' % name)
18
19
20 start_time=time.time()
21 g1=gevent.spawn(eat,'egon')
22 g2=gevent.spawn(play,'alex')
23
24 g1.join()
25 g2.join()
26 stop_time=time.time()
27 print(stop_time-start_time)
28 """
29 egon eat 1
30 alex play 1
31 egon eat 2
32 alex play 2
33 4.001747369766235
34
35 """
36 """
37 egon eat 1
38 egon eat 2
39 alex play 1
40 alex play 2
41 7.0017828941345215
42 """
43 """
44 egon eat 1
45 alex play 1
46 egon eat 2
47 alex play 2
48 4.001675367355347
49 """
50
51 from gevent import monkey;monkey.patch_all()
52 import gevent
53 import time
54
55
56 def eat(name):
57 print('%s eat 1' % name)
58 time.sleep(3)
59 print('%s eat 2' % name)
60
61
62 def play(name):
63 print('%s play 1' % name)
64 time.sleep(4)
65 print('%s play 2' % name)
66
67
68 g1=gevent.spawn(eat,'egon') # 异步操作
69 g2=gevent.spawn(play,'alex')
70
71 # time.sleep(5) # 得等到 全部执行完
72
73 # g1.join() # 等到 全部执行完
74 # g2.join()
75
76 gevent.joinall([g1,g2]) # 等到g1 g2 全部执行完
77 """
78 egon eat 1
79 alex play 1
80 egon eat 2
81 alex play 2
82 """
4.gevent实现并发的套接字通信
# 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
# 这里也说明了:单线程下面io问题降下来,效率大幅度提高
说明
使用:多进程
多线程
一个线程io 问题解决了 效率大大得提高
服务端:
1 #基于gevent实现
2 from gevent import monkey,spawn;monkey.patch_all()
3 from socket import *
4
5 def communicate(conn):
6 while True:
7 try:
8 data=conn.recv(1024)
9 if not data:break
10 conn.send(data.upper())
11 except ConnectionResetError:
12 break
13
14 conn.close()
15
16 def server(ip,port):
17 server = socket(AF_INET, SOCK_STREAM)
18 server.bind((ip,port))
19 server.listen(5)
20
21 while True:
22 conn, addr = server.accept()
23 spawn(communicate,conn) # 这里没必要加join
24
25 server.close()
26
27 if __name__ == '__main__':
28 g=spawn(server,'127.0.0.1',8090)
29 g.join()
客户端:
1 from socket import *
2 from threading import Thread,currentThread
3
4 def client(): #
5 client=socket(AF_INET,SOCK_STREAM)
6 client.connect(('127.0.0.1',8090))
7
8
9 while True:
10 client.send(('%s hello' %currentThread().getName()).encode('utf-8'))
11 data=client.recv(1024)
12 print(data.decode('utf-8'))
13
14 client.close()
15
16 # 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
17 # 这里也说明了:单线程下面io问题降下来,效率大幅度提高
18 if __name__ == '__main__':
19 for i in range(500): # 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
20 t=Thread(target=client)
21 t.start()