协程和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'))
客户端

 




posted @ 2018-07-17 20:21  Roc_Atlantis  阅读(277)  评论(0编辑  收藏  举报