10月17日学习内容整理:协程

一、协程的基本性质

1、概念:协程是由用户程序自己控制调度的,是一种用户态的轻量级线程

2、作用:用来实现单线程下实现并发,从应用程序级别控制单线程下多任务的切换,注意一定是遇到IO才切且不可能有并行的概念

3、优缺点

(1)优点:

》协程的切换速度相比操作系统切换速度要小得多

》隐藏自己的IO阻塞,使程序尽可能的处在就绪态,争取更多的执行权限,来“骗”操作系统识别不到线程的IO操作

(2)缺点:

》无法利用多核优势

》一旦协程出现不可挽回的IO阻塞(因为不可能真正的隐藏IO阻塞),会阻塞整个线程

4、特点:

》必须在单线程的前提下

》修改共享数据不用加锁

 

二、代码实现协程

1、greenlet模块(第三方库)

(1)方法

g=greenlet(函数名)  得到一个协程对象

g.switch(参数)  切换到g去执行,第一次切的时候传参数,后面再切换就不用传参了

(2)特点

但是,遇到IO阻塞是无法自动切换的,但是比yield切换更方便点

#greenlet与yield一样都无法检测io然后自动切换,唯一一点好处是:greenlet比yieled的切换方式更加方便
from  greenlet import greenlet
import time
def eat(name):
    print('%s eat 1' %name)
    time.sleep(10)
    g2.switch('alex')
    print('%s eat 2' %name)
    g2.switch()
def play(name):
    print('%s play 1' %name)
    g1.switch()
    print('%s play 2' %name)
g1=greenlet(eat)
g2=greenlet(play)
g1.switch('alex')

 

2、gevent模块(第三方库):真正实现切换的效果

(1)作用:能够做到自己检测IO,并实现自动切换

(2)方法:

g=gevent.spawn(函数名,参数)  得到一个协程对象,异步提交任务

g.join()   等到g结束

gevent.joinall([g1,g2])等g1,g2都结束,注意是列表形式

g.value  取g的运行结果,注意要join后或者是运行完后才能拿结果,spawn后立刻取结果可能是None

gevent.sleep()   也是睡几秒,相当于IO阻塞

gevent只能直接识别自己的IO阻塞,若是time.sleep()就不会直接识别也就不会自动切换,要想解决这个问题,就必须在导入gevent之前先加补丁:


from gevent import monkey
monkey.patch_all()

或者

from gevent import monkey;monkey.patch_all()

这样协程就能识别所有的IO阻塞

from gevent import monkey;monkey.patch_all()     #这样就可以识别所有的IO阻塞
import gevent
import os
from threading import current_thread
#1.检测IO
#2.自动切换
import time
def eat():
    print('%s eat 1' %current_thread().getName())
    time.sleep(2)
    print('%s eat 2' %current_thread().getName())
def play():
    print('%s play 1' %current_thread().getName())
    time.sleep(1)
    print('%s play 2' %current_thread().getName())

start=time.time()
g1=gevent.spawn(eat,)
g2=gevent.spawn(play,)

# g1.join()
# g2.join()
gevent.joinall([g1,g2])
stop=time.time()
print(stop-start)

 

3、基于协程的单线程并发

客户端:

from threading import Thread
from socket import *

def client():
    c=socket(AF_INET,SOCK_STREAM)
    c.connect(('127.0.0.1',8092))

    while True:
        c.send('hello'.encode('utf-8'))
        data=c.recv(1024)
        print(data.decode('utf-8'))

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

 

服务端:

from gevent import monkey;monkey.patch_all()
import gevent
from multiprocessing import Process
from socket import *

def server(ip,port):
    s = socket(AF_INET, SOCK_STREAM)
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind((ip,port))
    s.listen(5)
    while True:
        conn,addr=s.accept()
        print('%s:%s' % (addr[0], addr[1]))
        g1=gevent.spawn(talk,conn,addr)

def talk(conn,addr):
    while True:
        try:
            data=conn.recv(1024)
            print('%s:%s [%s]' %(addr[0],addr[1],data))
            if not data:break
            conn.send(data.upper())
        except ConnectionResetError:
            break
    conn.close()
if __name__ == '__main__':
    server('127.0.0.1',8092)

 

posted @ 2017-10-17 15:26  九二零  阅读(101)  评论(0编辑  收藏  举报