Python_协程

协程

又称微线程,纤程。英文名Coroutine。
协程是一种用户态的轻量级线程
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存在其他地方,在切回来的时候,
恢复先前保存的寄存器上下文和栈。因此,协程能够保存上一次调用时的状态(即所有局部状态的一个特定的组合)
每次过程重入时,就相当于进入上一次调用的状态
换种说法:进入上一次离开时所处逻辑流的位置。

协程的好处:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发,高扩展性,低成本:一个CPU支持上万个协程不是问题
缺点:
无法利用多核资源:协程的本质是个单线程,他不能同时将单个CPU的多个核用上,
协程需要和进程配合才能运行在多CPU上
进行阻塞操作会阻塞掉整个程序
  1. yield版协程
# Author : Xuefeng

import time
import queue

def consumer(name):
    '''
    定义消费者
    :param name: 
    :return: 
    '''
    print("----start eating meat--------")
    while True:
        # 循环生产肉
        new_meat = yield
        print("%s is eating meat %s" %(name, new_meat))
        time.sleep(1)

def producer():
    '''
    定义消费者
    :return: 
    '''
    # 触发yield
    r = con.__next__()
    r = con2.__next__()
    n = 0
    while n<5:
        n+=1
        con.send(n)
        con2.send(n)
        print("\033[41;1m [producer] \033[0m is making meat %s" %n)

if __name__=="__main__":
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()
  1. greenlet版协程 手动切换
# Author : Xuefeng

# 手动切换
from greenlet import greenlet

def test1():
    print(12)
    # 跳转下一个协程
    gr2.switch()
    # 跳回
    print(34)
    # 跳转
    gr2.switch()

def test2():
    print(56)
    # 跳转
    gr1.switch()
    # 跳回
    print(78)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

  1. gevent版协程 手动切换
# Author : Xuefeng

# 自动切换
import gevent

def foo():
    print("Running in foo")
    gevent.sleep(3)
    print("Explicit context switch to foo again")

def bar():
    print("Explicit switch to the bar")
    gevent.sleep(1)
    print("Implicit context in the bar")


def func():
    print("Explicit switch to the func")
    gevent.sleep(2)
    print("Implicit context in the func")

# 设置将要自动切换的协程
gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
    gevent.spawn(func)
])
  1. 利用协程实现简单的爬虫
# Author : Xuefeng

from urllib import request
import gevent
from gevent import monkey
import time

# 对所有io操作进行标记,相当于gevent.sleep,实现协程并发操作
monkey.patch_all()
def reptile(url):
    '''
    Define the function for reptile the website
    :param url: The address of the website
    :return:
    '''
    print("Reptile the %s" %url)
    info = request.urlopen(url)
    data = info.read()
    print(data)
    print("%d bytes has been get from %s" %(len(data), url))
    # print("\n")

# 串行执行
print("-----------one by one run--------")
start_time = time.time()
urls = [
    "http://www.baidu.com/",
    "http://www.sogou.com/",
    "http://www.meitu.com/"
]
for url in urls:
    reptile(url)
print("The one by one run cost %s time" %(time.time()-start_time))
print("\n")

# 并发执行
print("------------async_run-----------")
async_start = time.time()
gevent.joinall([
    gevent.spawn(reptile, "http://www.baidu.com/"),
    gevent.spawn(reptile, "http://www.sogou.com/"),
    gevent.spawn(reptile, "http://www.meitu.com/")
])
print("The async run cost %s time" %(time.time()-async_start))
  1. 用协程创建服务器与客户交互
    1) 服务器端
# Author : Xuefeng
import socket
import gevent
from gevent import monkey
# 对所有协程进行自动标记,帮助实现自动切换
monkey.patch_all()

def server(port):
    '''
    定义服务器端,设置链接,监听,等待并接收请求
    :param port: 端口号
    :return: 
    '''
    s = socket.socket()
    s.bind(('0.0.0.0', port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request, cli)

def handle_request(conn):
    '''
    定义处理请求函数,接收并发送数据
    :param conn: 建立的链接
    :return: 
    '''
    try:
        while True:
            # 接收数据
            data = conn.recv(1024)
            print("Recv:", data)
            # 发送数据
            conn.send(data)
            # 没有数据来到,断开链接
            if not data:
                conn.shutdown(socket.SHUT_WR)
    except Exception as e:
        print(e)
    finally:
        conn.close()

if __name__=="__main__":
    server(8001)

2) 客户端

# Author : Xuefeng

import gevent
import socket
from gevent import monkey

# 设置端口号与地址名称
HOST = "localhost"
PORT = 9000
# 创建socket链接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>>:"),encoding="utf-8")
    # 发送数据
    s.sendall(msg)
    # 接收数据
    data = s.recv(1024)
    # repr将数据格式化
    print("Recieved:", repr(data))
# 关闭链接
s.close()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值