协程和I/O模型
1、协程:
单线程实现并发
在应用程序里控制多个任务的切换+保存状态
优点:
应用程序级别速度要远远高于操作系统的切换
缺点:
多个任务一旦有一个阻塞没有切换,整个线程都阻塞在原地
该线程内的其他的任务都不能执行了
一旦引入协程,就需要检测单线程下所有的IO行为,
实现遇到IO就切换,少一个都不行,因为一旦一个任务阻塞了,整个线程就阻塞了,
其他的任务即便是可以计算,但是也无法运行了
2、协程的目的:
想要在单线程下实现并发
并发指的是多个任务看起来是同时运行的
并发=切换+保存状态
1 from gevent import monkey,spawn;monkey.patch_all()
2 from threading import current_thread
3 import time
4 # 实现了单线程下的并发,遇到i/O就切换
5 def eat():
6 print('%s eat 1' %current_thread().name)
7 time.sleep(3)
8 print('%s eat 2' %current_thread().name)
9
10 def play():
11 print('%s play 1' %current_thread().name)
12 time.sleep(1)
13 print('%s play 2' %current_thread().name)
14
15 start = time.time()
16 g1=spawn(eat,) # 创建一个协程对象
17 g2=spawn(play,)
18
19 print(current_thread().name)
20 g1.join() # 也有join方法
21 g2.join()
22
23 stop = time.time()
24 print(stop-start)
25
26 # MainThread
27 # DummyThread-1 eat 1 虚假线程
28 # DummyThread-2 play 1
29 # DummyThread-2 play 2
30 # DummyThread-1 eat 2
31 # 3.003624439239502
并发的套接字通信
1 from gevent import spawn,monkey;monkey.patch_all()
2 from socket import *
3 from threading import Thread
4
5 def talk(conn):
6 while True:
7 try:
8 data=conn.recv(1024)
9 if len(data) == 0:break
10 conn.send(data.upper())
11 except ConnectionResetError:
12 break
13 conn.close()
14
15 def server(ip,port,backlog=5):
16 server = socket(AF_INET, SOCK_STREAM)
17 server.bind((ip, port))
18 server.listen(backlog)
19
20 print('starting...')
21 while True:
22 conn, addr = server.accept()
23 spawn(talk, conn,) # 指定执行的任务,后面的conn是传入的参数
24
25
26 if __name__ == '__main__':
27 g=spawn(server,'127.0.0.1',8080) # 执行server
28 g.join()
1 from threading import Thread,current_thread
2 from socket import *
3 import os
4
5 def task():
6 client=socket(AF_INET,SOCK_STREAM)
7 client.connect(('127.0.0.1',8080))
8
9 while True:
10 msg='%s say hello' %current_thread().name
11 client.send(msg.encode('utf-8'))
12 data=client.recv(1024)
13 print(data.decode('utf-8'))
14
15 if __name__ == '__main__':
16 for i in range(500):
17 t=Thread(target=task)
18 t.start()
网络IO:
recvfrom:
wait data:等待客户端产生数据——》客户端OS--》网络--》服务端操作系统缓存
copy data:由本地操作系统缓存中的数据拷贝到应用程序的内存中
send:
copy data
# conn.recv(1024) ==>OS
非阻塞I/O模型
1 from socket import *
2 import time
3
4 server = socket(AF_INET, SOCK_STREAM)
5 server.bind(('127.0.0.1',8080))
6 server.listen(5)
7 server.setblocking(False)
8
9 conn_l=[]
10 while True:
11 try:
12 print('总连接数[%s]' % len(conn_l))
13 conn,addr=server.accept()
14 conn_l.append(conn)
15 except BlockingIOError:
16 del_l=[]
17 for conn in conn_l:
18 try:
19 data=conn.recv(1024)
20 if len(data) == 0:
21 del_l.append(conn)
22 continue
23 conn.send(data.upper())
24 except BlockingIOError:
25 pass
26 except ConnectionResetError:
27 del_l.append(conn)
28
29 for conn in del_l:
30 conn_l.remove(conn)
1 from socket import *
2 import os
3
4 client=socket(AF_INET,SOCK_STREAM)
5 client.connect(('127.0.0.1',8080))
6
7 while True:
8 msg='%s say hello' %os.getpid()
9 client.send(msg.encode('utf-8'))
10 data=client.recv(1024)
11 print(data.decode('utf-8'))
越是困难的事越要立即去做,这样收益才会最大!!!