python协程的实现

本文介绍了Python中的协程概念,通过示例展示了如何使用`yield`实现简单协程,以及greenlet库和gevent模块如何更高效地管理并发任务。还讨论了协程与进程、线程的区别,以及它们在处理IO操作和多任务中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python协程

1、协程,又称微线程,纤程。英文名Coroutine,协程并非python专属的概念,而是一个计算机概念,不同编程语言有不同的实现。
简单协程的实现

import time

def work1():
    while True:
        print("----work1---")
        yield 1
        time.sleep(0.5)

def work2():
    while True:
        print("----work2---")
        yield 2
        time.sleep(0.5)

def main():
    w1 = work1() 
    w2 = work2() 
    while True: 
        next(w1) 
        next(w2) 

if __name__ == "__main__": 
    main() 

  • yield 有让步之意,因为它交出了程序的控制权,但这个协程并没有结束,下一次执行时,将恢复到之前让出程序控制权的地方,也就是yield语句执行的地方继续执行。在上面的代码里,我创建了两个生成器(姑且认为,函数里有yiled就算生成器),也就是创建了两个协程,并且让这两个协程交替执行

想象一下,work1函数中,yield 语句执行的地方不是紧跟着一个整数,而是一个IO操作,那么这就会大大提高程序的并发。IO操作是阻塞的,耗时的,但协程可以在遇到IO操作的时候将程序的控制权让出,这个时候别的协程获得程序控制权继续执行。

一般的函数具备这样的功能么?正常我们在函数中,把程序控制权交出去,其本质不就是函数运行结束么?(使用return),无论如何也不会在中间某个位置交出控制权,过一段时间又回到这个位置继续执行,而协程就可以。

上面的代码,始终是在同一个线程内执行,这是很多人都会忽视的一个关键事实。多个协程在一个线程内协作,在协程间的切换不涉及到任何系统调用,也不会产生阻塞。

为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单

from greenlet import greenlet
import time  

def test1():  
    while True:  
        print("---A--")  
        gr2.switch()  
        time.sleep(0.5)  

def test2():  
    while True:  
        print("---B--")  
        gr1.switch()  
        time.sleep(0.5)  

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

#切换到gr1中运行  
gr1.switch() #通过调用gr1的switch方法,我们启动了gr1协程。因为gr1内部有一个while True循环,并且会不断地切换到gr2,而gr2也会不断地切换回gr1,所以这两个协程会交替执行,从而不断地在控制台打印出"---A--"和"---B--"。



  • 1、 协程与进程、线程的不同
    • 进程、线程 创建完之后,到底是哪个进程、线程执行 不确定,这要让操作系统来进行计算(调度算法,例如优先级调度)
    • 协程是可以人为来控制的
  • 2、greenlet库提供的协程是完全由用户控制的,这意味着我们需要显式地调用switch方法来在协程之间进行切换。这与Python内置的async/await语法(用于异步编程)有所不同,后者提供了更高级别的协程控制,并且可以自动处理协程之间的切换。
import gevent


def f1(n):
    for i in range(n):
        print("-----f1-----", i)


def f2(n):
    for i in range(n):
        print("-----f2-----", i)


def f3(n):
    for i in range(n):
        print("-----f3-----", i)


g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()  # join会等待g1标识的那个任务执行完毕之后 对其进行清理工作,其实这就是一个 耗时操作
g2.join()# 等待g2标识的协程(即f2函数的执行)完成
g3.join()# 等待g3标识的协程(即f3函数的执行)完成

在这里插入图片描述

  • 1、可以看到,3个greenlet是依次运行而不是交替运行

  • 2、使用gevent来实现多任务的时候,有一个很特殊的地方它可以自行切换协程指定的任务,而且切换的前提是:当一个任务用到耗时操作(例如延时),它就会把这个时间拿出来去做另外的任务,这样做最终实现了多任务 而且自动切换,如下

import gevent


def f1(n):
    for i in range(n):
        print("-----f1-----", i)
        gevent.sleep(1)


def f2(n):
    for i in range(n):
        print("-----f2-----", i)
        gevent.sleep(1)


def f3(n):
    for i in range(n):
        print("-----f3-----", i)
        gevent.sleep(1)


g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()  # join会等待g1标识的那个任务执行完毕之后 对其进行清理工作,其实这就是一个 耗时操作
g2.join()
g3.join()

# 使用gevent来实现多任务的时候,有一个很特殊的地方
# 它可以自行切换协程指定的任务,而且切换的前提是:当一个任务用到耗时操作(例如延时),它就会把这个时间拿出来去做另外的任务
# 这样做最终实现了多任务 而且自动切换

在这里插入图片描述
3、并发下载视频
1、可以将url换为自己需要下载视频、音乐、图片等网址,重新执行即可完成下载

from gevent import monkey
import gevent
import urllib.request
import ssl

monkey.patch_all()

ssl._create_default_https_context = ssl._create_unverified_context


def my_downLoad(file_name, url):
    print('GET: %s' % url)
    resp = urllib.request.urlopen(url)
    data = resp.read()

    with open(file_name, "wb") as f:
        f.write(data)

    print('%d bytes received from %s.' % (len(data), url))


gevent.joinall([
    gevent.spawn(my_downLoad, "1.mp4", '视频URL地址'),
    gevent.spawn(my_downLoad, "2.mp4", '视频URL地址2')
])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值