协程

一、协程

1、协程:
单线程实现并发(为了提高效率;但不是说所有协程都会提升效率)
在应用程序里控制多个任务的切换+保存状态
优点:
应用程序级别速度要远远高于操作系统的切换
缺点:
多个任务一旦有一个阻塞没有切,整个线程都阻塞在原地
该线程内的其他的任务都不能执行了

一旦引入协程,就需要检测单线程下所有的IO行为,
实现遇到IO就切换,少一个都不行,因为一旦一个任务阻塞了,整个线程就阻塞了,
其他的任务即便是可以计算,但是也无法运行了

2、有效的协程在一定程度‘骗过’了CPU;通过自己内部协调,一遇到IO就切到自己的其他程序中,
使得CPU以为这个程序一直在运行,从而使其更有可能处于就绪态或运行态,以更多的占用CPU。

实现并发的三种手段:
a)单线程下的并发;由程序自己控制,相对速度快
b)多线程下的并发;由操作系统控制,相对速度较慢
c)多进程下的并发;由操作系统控制,相对速度慢

3、协程序的目的:
想要在单线程下实现并发
并发指的是多个任务看起来是同时运行的
并发=切换+保存状态

yield、greenlet都无法实现遇到IO自动切换来提高效率 ,就用到了gevent模块


from gevent import monkey,spawn;monkey.patch_all() #monkey.patch_all()打补丁(gevent)
from threading import current_thread
import time

def eat():
print('%s eat 1'%current_thread().name)
time.sleep(3) # 直接调用gevent模块实现遇到IO切换+保持状态
print('%s eat 2'%current_thread().name)

def play():
print('%s play 1'%current_thread().name)
time.sleep(1)
print('%s play 2 '%current_thread().name)

start=time.time()
g1=spawn(eat,) #spawn 默认为异步调用,如果不加time.sleep或 g.join(),spawn提交后不在原地等待执行,程序直接结束
g2=spawn(play,)

print(current_thread().name)
g1.join()
g2.join()
print('主',time.time()-start)

'''
MainThread
DummyThread-1 eat 1
DummyThread-2 play 1
DummyThread-2 play 2
DummyThread-1 eat 2
主 3.006171941757202
'''
本质是一个线程 (Dummy虚拟的,假的)


二、并发的套接字通信

服务端:
from socket import *
from gevent import spawn,monkey;monkey.patch_all()
from threading import Thread

def talk(conn,):
while True:
try:
data=conn.recv(1024)
if len(data)==0:break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()

def server(ip,port,backlog=5):
server=socket(AF_INET,SOCK_STREAM)
server.bind((ip,port))
server.listen(backlog)

print('start...')
while True:
conn,client_addr=server.accept()
spawn(talk,conn,)

if __name__ == '__main__':
g=spawn(server,'127.0.0.1',8080)
g.join()


客户端:
from threading import Thread,current_thread
from socket import *
import os

def task():
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))

while True:
msg='%s say hello' %current_thread().name
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))

if __name__ == '__main__':
for i in range(500):
t=Thread(target=task,)
t.start()