协程

#############################  协 程  #######################################
进程 : 资源分配的最小单位 ,班级
线程: cpu调度最小单位 , 人
什么是协程? 一我的分八半
协程 :能在一条线程的基础上,在多个任务之间互相切换
节省了线程的开启的消耗
是从python代码的级别调度的
正常的线程是CPU调度的最小单位
协程的调度并非由操做系统来完成的
在两个任务之间互相切换-协程
def func():
    print(1)
    x = yield 'aaa'
    print(x)
    yield 'bbb'

g = func()
print(next(g))       ##接收的返回值
print(g.send(****))    ##传给了x

#获得结果:
                1
                aaa
                ****
                bbb


在多个函数之间互相切换的功能  -协程
def consumer():
        while True:
            x = yield
            print(x)
def producer():
        g = consumer()
        next(g)       #预激
        for i in range(10):
                g.send(i)
producer()

yield 只有程序之间的切换,没有重利用任何IO操做的时间 
切换

须要强调的是:
#1.python的线程属于内核级别的,即由操做系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其余线程运行)
#2.单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操做系统)控制切换,以此来提高效率(!!!非io操做的切换与效率无关)

对比操做系统控制线程的切换,用户在单线程内控制协程的切换python

优势:
#1,协程的切换开销更小, 属于程序级别的切换,操做系统彻底感知不到,于是更加轻量级
#2,单线程内就能够实现并发的效果,最大限度的利用cpu

缺点:
#1,协程的本质是单线程下,没法利用多核,能够是一个程序开启多个进程,每一个进程内开启多个线程,每一个线程内开启协程
#2,协程指的是单个线程,于是一旦协程出现阻塞,将会则色整个线程

总结协程特色:安全

  1.必须在只有一个单线程里实现并发服务器

  2.修改共享数据不需加锁并发

  3.用户程序里本身保存多个控制流的上下文栈socket

  4.附加:一个协程遇到IO操做自动切换到其余协程(如何实现检测IO,yield,greenlet都没法实现,就用到了gevent模块(select机制))ide

 

 

Greenlet模块函数

安装 pip3 install greenletspa

import time
from greenlet import greenlet
# 协程模块  # 单纯的程序切换耗费时间
def eat():
    print('')
    time.sleep(1)
    g2.switch()  # 切换
    print('吃完了')
    time.sleep(1)
    g2.switch()

def play():
    print('玩儿')
    time.sleep(1)
    g1.switch()
    print('玩儿美了')
    time.sleep(1)

g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()   # 切换
greenlet 切换

单纯的切换(在没有io的状况下或者没有重复开辟内存空间的操做),反而会下降程序的执行速度操作系统

#顺序执行
import time
def f1():
    res=1
    for i in range(100000000):
        res+=i

def f2():
    res=1
    for i in range(100000000):
        res*=i

start=time.time()
f1()
f2()
stop=time.time()
print('run time is %s' %(stop-start)) #10.985628366470337

#切换
from greenlet import greenlet
import time
def f1():
    res=1
    for i in range(100000000):
        res+=i
        g2.switch()

def f2():
    res=1
    for i in range(100000000):
        res*=i
        g1.switch()

start=time.time()
g1=greenlet(f1)
g2=greenlet(f2)
g1.switch()
stop=time.time()
print('run time is %s' %(stop-start)) # 52.763017892837524
效率对比

greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时若是遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提高效率的问题线程

单线程里的这20个任务的代码一般会既有计算操做又有阻塞操做,咱们彻底能够在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提升效率,这就用到了Gevent模块

 

Gevent模块

安装;  pip3 install gevent

g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面能够有多个参数,能够是位置实参或关键字实参,都是传给函数eat的

g2=gevent.spawn(func2)

g1.join() #等待g1结束

g2.join() #等待g2结束

#或者上述两步合做一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值
用法介绍
#使用协程减小IO操做带来的时间消耗
from gevent import monkey;monkey.patch_all()
import gevent
import time

def eat():
    print('')
    time.sleep(2)
    print('吃完了')

def play():
    print('玩儿')
    time.sleep(1)
    print('玩儿美了')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1,g2])      ##至关于 g1.join()  和g2.join()的结合       

#要加join的  不加join的话 不会有结果

#gevent帮你作了切换,作切换是由条件的,遇到IO才切换
#gevent 不认识除了gevent这个模块内意外的IO操做
#使用join能够一直阻塞直到协程任务完成

#帮助gevent来认识其余模块中的阻塞,就须要把  
# from gevent import monkey;monkey.path_all() 写在其余模块导入以前
例子

 

上例gevent.sleep(2)模拟的是gevent能够识别的io阻塞,而time.sleep(2)或其余的阻塞,gevent是不能直接识别的须要用下面一行代码,打补丁,就能够识别了

from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块以前

或者咱们干脆记忆成:要用gevent,须要将from gevent import monkey;monkey.patch_all()放到文件的开头

 

协程完成的socket

from gevent import monkey;monkey.patch_all()
import socket
import gevent

def talk(conn):
    while True:
        conn.send(b'hello')
        print(conn.recv(1024))

sk = socket.socket()
sk.bind(('127.0.0.1',9527))
sk.listen()

while True:
    conn,addr = sk.accept()
    gevent.spawn(talk,conn)
server 服务器
import socket
from threading import Thread
def client():
    sk = socket.socket()
    sk.connect(('127.0.0.1',9527))
    while True:
        print(sk.recv(1024))
        sk.send(b'bye')

for i in range(500):
    Thread(target = client).start()
client 客户端

协程:   可以在单核的状况下,极大地提升CPU的利用率

    不存在数据不安全

    也不存在线程切换\创造的时间开销

    切换是用户级别的,程序不会由于协程中某一个任务进入阻塞状态而使整条线程阻塞

线程的切换:

    时间片到了, 下降CPU的效率

    IO回切     提升CPU的效率

相关文章
相关标签/搜索