python 之协程

协程:是单线程下的并发,又称微线程。

什么是线程?:

协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

协程的本质

协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率

对于不涉及io的操作,单纯的切换反而会降低效率

#并发执行
import time


def producer():
    g = consumer()
    next(g)
    for i in range(10000000):
        g.send(i)


def consumer():
    while True:
        res = yield


start_time = time.time()
producer()
stop_time = time.time()
print(stop_time-start_time)



#串行
import time

def producer():
    res=[]
    for i in range(10000000):
        res.append(i)
    return res


def consumer(res):
    pass


start_time=time.time()
res=producer()
consumer(res)
stop_time=time.time()
print(stop_time-start_time)

总结协程的优缺点:

优点

1、协程开销小,属于程序级别的切换,操作系统感知不到

2、单线程下可以实现并发效果, 最大限度地利用CPU

缺点

1、协程的本质是单线程下,无法利用多核优势。

2、协程是指单线程,一旦协程出现阻塞,将会阻塞整个线程。

协程的特点:

1、必须在一个单线程里实现并发

2、修改共享数据不加锁

3、用户程序自己控制保存上下文

4、一个协程遇到io操作自动切换到其他协程。

grennlet模块

并不能监听(不能遇到io自动切换)

from greenlet import greenlet


def func1():
    print('123')
    g2.switch()
    print('456')


def func2():
    print('abc')
    g1.switch()
    print('qqq')
    g1.switch()


if __name__ == '__main__':
    g1 = greenlet(func1)
    g2 = greenlet(func2)
    g1.switch()

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自动切换

import gevent
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

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


g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')
同步与异步的性能比较
import gevent
import time


def task(i):
    gevent.sleep(1)
    
    print(i)
    
    # res = 0
    # for i in range(1000):
    #     res = i**i
    # return res


def async():
    threads = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(threads)
    
    
def sync():
    for i in range(10):
        gevent.spawn(task, i)
 
        
t1 = time.time()
sync()
t2 = time.time()
async()
t3 = time.time()
print('sync run time', t2 - t1)
print('asyn run time', t3 - t2)
协程实现FTP

服务端

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

def read(conn):
    try:
        while True:
            data = conn.recv(1024).decode()
            if not data:
                break
            conn.send(data.upper().encode())
    except Exception as e:
        print(e)
        conn.close()


if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 9999))
    server.listen(10)
    while True:
        conn, addr = server.accept()
        gevent.spawn(read, conn)

客户端

import socket

client = socket.socket()
client.connect(('127.0.0.1', 9999))
while True:
    msg = input('>>').strip()
    if not msg: continue
    client.send(msg.encode())
    data = client.recv(1024).decode()
    print(data)

协程实现生产者消费者模型

import queue
import gevent


def comsumer():
    print("消费者")
    while True:
        gevent.sleep(0.1)
        res = q.get()
        # print('res', res)
        if not res:
            break
        print('消耗了', res)


def producer(n):
    print("生产者")
    for i in range(1, 10):
        q.put(i)
        print('%s生产了%s' % (n, i))


if __name__ == '__main__':
    q = queue.Queue()
    p1 = gevent.spawn(producer, 'p1')
    p2 = gevent.spawn(producer, 'p2')
    p3 = gevent.spawn(producer, 'p3')
    c1 = gevent.spawn(comsumer)

    p1.join()
    p2.join()
    p3.join()
 
    q.put(0)
    c1.join()
    print('zhu')
原文地址:https://www.cnblogs.com/Jason-lin/p/8545251.html