Python协程

一、协程介绍##

1、概念
协程是单线程下的并发,又称为微线程
它是一种用户态的轻量级线程
协程能保留上一次调用时的状态
进程和线程时通过操作系统(cpu来调度),而协程是由程序员来调度控制的
2、协程的意义:
多线程:分时切片,并发操作,线程切换需要耗时(保存状态)
协程: 只使用一个线程,在一个线程中规定某个代码块的执行顺序
3、应用场景:
因为Python有GIL(全局解释器锁)的存在,在同一时间内实际只有一个线程在工作(IO操作保存状态、切换耗时),多线程的执行效率比协程会更低。


84a91f4f66a9d235ca5178ef36e73f4.png
  • 高并发I/O场景:如爬虫、聊天应用、HTTP服务器。
  • 异步编程:如Python中的asyncio、JavaScript中的async/await。
  • 数据流处理:处理大量并发任务时,协程能显著提高效率
    4、协程特点:
  • 轻量级:协程无需像线程一样依赖操作系统进行上下文切换,由程序自行调度
  • 非抢占式: 协程是协作式的,由代码显式控制何时出让执行器
  • 单线程多任务:在一个线程内实现多个任务之间的切换
  • 异步和高效:协程可以暂停当前任务并切换到其他任务,适合 I/O 密集型任务

二、协程创建

1、yield

1)概念:

  • yield是python中的一个关键字,用于在函数中生成一个生成器(Generator)
  • 一个包含yield的函数不是普通函数,而是一个生成器函数
  • 调用生成器函数时,返回的是一个生成器对象,而不是一个函数执行的内容
    2)作用:
  • 暂停函数执行:每次遇到yield,函数会将当前状态(变量、执行位置)保存,并暂停执行
  • 返回值:yield会将值返回给调用方
  • 恢复执行:下次从暂停的函数继续运行,而不是从头开始
    3) yield使用示例:
def study():
    print("hello,python")
    yield 1
    print("hello,java")
    yield 2
    
gen = study()
print(next(gen))
print(next(gen))
image.png

4)生成器和协程

  • 生成器是协程的基础:
    • yield 可以暂停函数的执行。
    • 使用 send() 方法可以向协程发送数据。
  • 协程与生成器的区别:
    • 生成器通过 yield 生成值。
    • 协程通过 yield 接收和发送数据。

5)yield实现的简单协程

def simple_coroutine():
    print("create coroutine")
    # 等待接收外部发送的数据
    x = yield
    print(f"-> Coroutine received: {x}")

# 创建协程对象
coroutine = simple_coroutine()
# 启动协程
next(coroutine)
# 发送数据到协程
coroutine.send("hello")
image.png

2、greenlet

from greenlet import greenlet


def speak():
    print("我要说。。。")
    g1.switch()
    print("周末去哪里玩呢")
    g1.switch()


def study():
    print("我要学习")
    g2.switch()
    print("学习编程")
    g2.switch()


g1 = greenlet(study)
g2 = greenlet(speak)
g1.switch() #启动g1
image.png

3、gevent

gevent是一个基于协程的 Python 异步编程库,专注于简化并发任务的处理。它通过猴子补丁(monkey patching)将标准库中的阻塞调用替换为非阻塞版本,使程序可以并发执行 I/O 操作。

  • Gevent 是基于 Greenlet 实现的一个更高级的协程库,支持异步网络通信。
  • 使用 Gevent 可以自动处理任务调度,不需要手动调用 switch()。

1)基本函数介绍:

  • spawn(func,kwargs):创建协程对象
  • join():直到这个协程执行完毕
  • joinall()
  • sleep(): 简洁的 API 模拟阻塞行为
    2) 示例:
import gevent

data = []

def write():
    for i in range(5):
        data.append(i)
        print("添加数据:",i)
        gevent.sleep(1)
        print("添加后:",data)


def read():
    for i in range(5):
        print("read:",data[i])
        gevent.sleep(1)
        print("===")

t1 = gevent.spawn(write)
t2 = gevent.spawn(read)
# 等待所有协程完成
gevent.joinall([t1, t2])

image.png

3)、猴子补丁(Monkey Patching)
Gevent 提供了 monkey.patch_all() 方法,自动将标准库中的阻塞函数(如 time.sleep、socket)替换为非阻塞版本。

import gevent
from gevent import monkey
import  time


data = []

def write():
    for i in range(5):
        data.append(i)
        print("添加数据:",i)
        time.sleep(1)
        print("添加后:",data)
    print("函数写入完毕")



def read():
    for i in range(5):
        print("read:",data[i])
        time.sleep(1)
        print("===")
    print("函数读取完毕")

t1 = gevent.spawn(write)
t2 = gevent.spawn(read)

# 打开猴子补丁
# 注意这个位置要在join之前打开
# monkey.patch_all()

# 等待所有协程完成
gevent.joinall([t1, t2])
image.png

不开猴子补丁时,gevent并不认识time这种阻塞的函数,就当作普通函数来处理,按顺序执行

import gevent
from gevent import monkey
import  time


data = []

def write():
    for i in range(5):
        data.append(i)
        print("添加数据:",i)
        time.sleep(1)
        print("添加后:",data)
    print("函数写入完毕")



def read():
    for i in range(5):
        print("read:",data[i])
        time.sleep(1)
        print("===")
    print("函数读取完毕")

t1 = gevent.spawn(write)
t2 = gevent.spawn(read)

# 打开猴子补丁
# 注意这个位置要在join之前打开
monkey.patch_all()

# 等待所有协程完成
gevent.joinall([t1, t2])
image.png

image.png

实现了边写边读,自动切换
4)Gevent与I/0操作,并发请求多个URL

import gevent
from gevent import monkey
monkey.patch_all()  # 打开猴子补丁
import requests

urls = [
    "https://www.google.com",
    "https://www.python.org",
    "https://www.github.com"
]

def fetch_url(url):
    print(f"Fetching: {url}")
    response = requests.get(url)
    print(f"{url}: {len(response.text)} bytes received")

gevent.joinall([
    gevent.spawn(fetch_url, url) for url in urls
])

image.png

三、进程、线程、协程比对

image.png

c19ac6dbffa202bc3d68da46efb40da.png

四、实际应用场景

image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容