并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信

Posted Alice的小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信相关的知识,希望对你有一定的参考价值。

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()

 

以上是关于并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信的主要内容,如果未能解决你的问题,请参考以下文章

Python并发编程——多线程与协程

17第七周-网络编程 - 协程概念介绍协程gevent模块并发爬网页

Go 并发编程之协程及其调度机制

并发编程协程(Coroutine)之Gevent

测开之并发编程篇・《并发并行线程队列进程和协程》

并发编程(协程)