python协程

 

协程概念

协程,又称微线程,纤程。英文Coroutine。协程是一种用户态的轻量级线程。

线程是系统级别,由操作系统调度;协程是程序级别,由程序员根据需要自行调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行其他的子程序;其他的子程序也可以中断回来继续执行之前的子程序,此之谓协程。

比较专业的理解:

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

协程实现

yield实现实例

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print(f'[consumer] consuming {n}')
        r = '2 000K'

def producer(c):
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print(f'[producer] producing {n}')
        r = c.send(n)
        print(f'[producer] consuming return: {r}')
    c.close()

c = consumer()
producer(c)

import collections
import queue
import random
******************************************************************************
Event = collections.namedtuple('Event', 'time proc action')

def taxi_process(proc, trips_num, start_time=0):
    """
    :param proc:
    :param trips_num:
    :param start_time:
    :return:
    """
    time = yield Event(start_time, proc, 'leave garage')
    for i in range(trips_num):
        time = yield Event(time, proc, 'pick up people')
        time = yield Event(time, proc, 'drop off people')

    yield Event(time, proc, 'go home')

class SimulateTaxi():
    """
    模拟出租车控制台
    """
    def __init__(self, proc_map):
        # 保存排定事件的PriorityQueue对象
        # 如果是tuple类型,默认使用tuple[0]
        self.events = queue.PriorityQueue()
        # procs_map 是dict类型,使用dict构建本地副本
        self.procs = dict(proc_map)

    def run(self, end_time):
        """
        排定并打印事件流程,直到时间结束
        :param end_time:
        :return:
        """
        for _, taxi_gen in self.procs.items():
            leave_evt = next(taxi_gen)
            self.events.put(leave_evt)

        # 仿真系统主循环
        simulate_time = 0
        while simulate_time < end_time:
            if self.events.empty():
                print('*** end of events ***')
                break

            # 第一个事件
            current_evt = self.events.get()
            simulate_time, proc, action = current_evt
            print(f'taxi: {proc}, at time: {simulate_time}, {action}')

            # 准备下个事件发生
            proc_gen = self.procs[proc]
            next_simulate_time = simulate_time + self.compute_duration()

            try:
                next_evt = proc_gen.send(next_simulate_time)
            except StopIteration:
                del self.procs[proc]
            else:
                self.events.put(next_evt)
        else:
            msg = '*** end of simulate'
            print(msg.format(self.events.qsize()))

    @staticmethod
    def compute_duration():
        """
        随机产生下个事件发生时间
        :return:
        """
        duration_time = random.randint(1, 20)
        return duration_time

# 生成3个出租车,现在全部没有离开车库
taxies = {i: taxi_process(i, (i + 1) * 2, i *5) for i in range(3)}

# 模拟运行
st = SimulateTaxi(taxies)
st.run(100)

******************************************************************

greenlet实现

    greenlet需要手动切换去执行其他的子程序,在其他的子程序中又主动切换回来。

#! /usr/bin/env python
# -*- coding:utf-8 -*-

from greenlet import greenlet
# greenlet 其实就是手动切换;gevent是对greenlet的封装,可以实现自动切换

def test1():
    print("123")
    gr2.switch()   # 切换去执行test2
    print("456")
    gr2.switch()   # 切换回test2之前执行到的位置,接着执行

def test2():    
    print("789")
    gr1.switch()   # 切换回test1之前执行到的位置,接着执行
    print("666")


gr1 = greenlet(test1)   # 启动一个协程 注意test1不要加()
gr2 = greenlet(test2)   #
gr1.switch()

 

gevent实现

  gevent是一个三方库,可以轻松通过gevent实现协程,在gevent中用到的主要模式Greenlet,它以C扩展模块形式接入Python轻量级协程。Greenlet运行于主程序操作系统内部,但它们被协作式调度。

   gevent会主动识别程序内部IO操作,当子程序遇到IO时,切换至其他的子程序。如果所有的子程序都进入IO,则阻塞。

实例

import gevent

def func1():
    print('func1 running')
    gevent.sleep(5)   # 内部实现IO
    print('switch func1')

def func2():
    print('func2 running')
    gevent.sleep(3)   # 内部实现IO
    print('switch func2')

def func3():
    print('func1 running')
    gevent.sleep(0)   # 内部实现IO
    print('switch func3')

gevent.joinall([gevent.spawn(func1),
                    gevent.spawn(func2),
                    gevent.spawn(func3),
                    ])

gevent爬虫版

import requests
import gevent, time
from gevent import monkey

monkey.patch_all() # 把当前程序中所有io操作都做上标记

def spider(url):
    print('Get {}'.format(url))
    res = requests.get(url)
    data = res.text
    print('{}s bytes received from {}...'.format(len(data), url))

urls = [
    "https://www.python.org/",
    "https://www.yahoo.com/",
    "https://github.com/"
]
start_time = time.time()
for url in urls:
    spider(url)
print('同步耗时: ',time.time()-start_time)

start_time = time.time()
gevent.joinall([gevent.spawn(spider, url) for url in urls])
print('异步耗时: ',time.time()-start_time)

socket版

server

# import socket
import gevent
from gevent import socket, monkey
monkey.patch_all()

def server(port):
    s = socket.socket()
    s.bind(('127.0.0.1', port))
    s.listen(10)
    print('开始服务...')
    while True:
        conn, addr = s.accept()
        gevent.spawn(handle_request, conn)

def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024).decode()
            print('recv: ',data)
            reply = input('>>:')
            conn.send(reply.encode())
            if not data:
                conn.shutdown(socket.SHUT_WR)

    except Exception as e:
        print(e)
    finally:
        conn.close()

if __name__ == '__main__':
    server(8888)

client

import socket

client = socket.socket()
client.connect(('localhost', 8888))
while True:
    msg = input('>>:')
    client.send(msg.encode())
    data = client.recv(1024).decode()
    print('server: ', data)

client.close()

                                                              转自:https://www.cnblogs.com/zingp/p/5911537.html 协程及Python中的协程

你可能感兴趣的:(python协程)