当前位置:首页 » 编程语言 » python协程的实现原理

python协程的实现原理

发布时间: 2022-12-25 06:27:33

㈠ 简述python进程,线程和协程的区别及应用场景

协程多与线程进行比较
1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。
2) 线程进程都是同步机制,而协程则是异步
3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

㈡ python协程为什么不需要枷锁

Python是一门动态的脚本语言,它有一个锁叫做全局解释器锁,它这个锁是加在cpython解释器上的,我们说的Python多线程,再线程切换的时候加了锁,用了控制同步。所以多线程不是真正意义的并发,而协程是在线程里面的,线程并没有锁,一个线程可以有多个协程,协程又叫微线程,它的切换完全由自己创建,它有几种实现方式,一种是yield和send,一种是gevent,一种是greenlet,线程的并发不好,协程可以有上万次并发。回到之前的问题,因为协程在线程内,而线程本身没有锁,所以携程没有锁。

㈢ Python(七十二)多任务异步协程

11_lxml/01_线程池的基本使用.py:

11_lxml/02_异步.py:

11_多任务异步协程/03_多任务异步.py:

11_多任务异步协程/04_flask服务.py:

11_多任务异步协程/04_多任务异步协程.py:

11_多任务异步协程/05_aiohttp实现.py:(需与11_多任务异步协程/05_aiohttp实现.py连用)

文章到这里就结束了!希望大家能多多支持Python(系列)!六个月带大家学会Python,私聊我,可以问关于本文章的问题!以后每天都会发布新的文章,喜欢的点点关注!一个陪伴你学习Python的新青年!不管多忙都会更新下去,一起加油!

Editor:Lonelyroots

㈣ 协程与异步IO

协程,又称微线程,纤程。英文名 Coroutine 。Python对协程的支持是通过 generator 实现的。在generator中,我们不但可以通过for循环来迭代,还可以不断调用 next()函数 获取由 yield 语句返回的下一个值。但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。yield其实是终端当前的函数,返回给调用方。python3中使用yield来实现range,节省内存,提高性能,懒加载的模式。

asyncio是Python 3.4 版本引入的 标准库 ,直接内置了对异步IO的支持。

从Python 3.5 开始引入了新的语法 async 和 await ,用来简化yield的语法:

import asyncio

import threading

async def compute(x, y):

    print("Compute %s + %s ..." % (x, y))

    print(threading.current_thread().name)

    await asyncio.sleep(x + y)

    return x + y

async def print_sum(x, y):

    result = await compute(x, y)

    print("%s + %s = %s" % (x, y, result))

    print(threading.current_thread().name)

if __name__ == "__main__":

    loop = asyncio.get_event_loop()

    tasks = [print_sum(1, 2), print_sum(3, 4)]

    loop.run_until_complete(asyncio.wait(tasks))

    loop.close()

线程是内核进行抢占式的调度的,这样就确保了每个线程都有执行的机会。而 coroutine 运行在同一个线程中,由语言的运行时中的 EventLoop(事件循环) 来进行调度。和大多数语言一样,在 Python 中,协程的调度是非抢占式的,也就是说一个协程必须主动让出执行机会,其他协程才有机会运行。

让出执行的关键字就是 await。也就是说一个协程如果阻塞了,持续不让出 CPU,那么整个线程就卡住了,没有任何并发。

PS: 作为服务端,event loop最核心的就是IO多路复用技术,所有来自客户端的请求都由IO多路复用函数来处理;作为客户端,event loop的核心在于利用Future对象延迟执行,并使用send函数激发协程,挂起,等待服务端处理完成返回后再调用CallBack函数继续下面的流程

Go语言的协程是 语言本身特性 ,erlang和golang都是采用了CSP(Communicating Sequential Processes)模式(Python中的协程是eventloop模型),但是erlang是基于进程的消息通信,go是基于goroutine和channel的通信。

Python和Go都引入了消息调度系统模型,来避免锁的影响和进程/线程开销大的问题。

协程从本质上来说是一种用户态的线程,不需要系统来执行抢占式调度,而是在语言层面实现线程的调度 。因为协程 不再使用共享内存/数据 ,而是使用 通信 来共享内存/锁,因为在一个超级大系统里具有无数的锁,共享变量等等会使得整个系统变得无比的臃肿,而通过消息机制来交流,可以使得每个并发的单元都成为一个独立的个体,拥有自己的变量,单元之间变量并不共享,对于单元的输入输出只有消息。开发者只需要关心在一个并发单元的输入与输出的影响,而不需要再考虑类似于修改共享内存/数据对其它程序的影响。

㈤ python协程gevent怎么用

在学习gevent之前,你肯定要知道你学的这个东西是什么。

官方描述gevent

gevent is a coroutine-based Python networking library that uses greenlet to provide a high-level synchronous API on top of the libev event loop.

翻译:gevent是一个基于协程的Python网络库。我们先理解这句,也是这次学习的重点——协程。

wiki描述协程

与子例程一样,协程也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。子例程的起始处是惟一的入口点,一旦退出即完成了子例程的执行,子例程的一个实例只会返回一次;协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。协程允许多个入口点,可以在指定位置挂起和恢复执行。

没看懂?没关系,我也没看懂,不过算是有点线索:子例程。

子例程

过程有两种,一种叫子例程(Subroutine),通常叫Sub;另一种叫函数(Function)。底层实现机制是一样的,区别在于,Sub只执行操作,没有返回值;Function不但执行操作,并且有返回值。用过VB的应该会比较清楚这点。(原谅我用了网络)说到底子例程就是过程,我们一般叫它函数。

说到函数,我就想吐槽了,不明白为什么要叫函数。很多时候我们写一个函数是为了封装、模块化某个功能,它是一个功能、或者说是一个过程。因为它包含的是类似于流程图那样的具体逻辑,先怎样做,然后怎样做;如果遇到A情况则怎样,如果遇到B情况又怎样。个人觉得还是叫过程比较好,叫做函数就让人很纠结了,难道因为回归到底层还是计算问题,出于数学的角度把它称为函数?这个略坑啊!为了符合大家的口味,我还是称之为函数好了(其实我也习惯叫函数了%>_

讲到函数,我们就往底层深入一点,看看下面的代码:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

def a():

print "a start"

b()

print "a end"

def b():

print "b start"

c()

print "b end"

def c():

print "c start"

print "c end"

if __name__ == "__main__":

a()

a start

b start

c start

c end

b end

a end

对于这样的结果大家肯定不会意外的。每当函数被调用,就会在栈中开辟一个栈空间,调用结束后再回收该空间。

假设一个这样的场景:有个讲台,每个人都可以上去发表言论,但是每次讲台只能站一个人。现在a在上面演讲,当他说到“大家好!”的时候,b有个紧急通知要告诉大家,所以a就先下来让b讲完通知,然后a再上讲台继续演讲。如果用函数的思想模拟这个问题,堆栈示意图是这样的:

那什么东西有这样的能力呢?我们很快就可以想到进程、线程,但是你真的想使用进程、线程如此重量级的东西在这么简单的程序上吗?野蛮的抢占式机制和笨重的上下文切换!

还有一种程序组件,那就是协程。它能保留上一次调用时的状态,每次重新进入该过程的时候,就相当于回到上一次离开时所处逻辑流的位置。协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。协程的生命期完全由他们的使用的需要决定。每个协程在用yield命令向另一个协程交出控制时都尽可能做了更多的工作,放弃控制使得另一个协程从这个协程停止的地方开始,接下来的每次协程被调用时,都是从协程返回(或yield)的位置接着执行。

从上面这些你就可以知道其实协程是模拟了多线程(或多进程)的操作,多线程在切换的时候都会有一个上下文切换,在退出的时候将现场保存起来,等到下一次进入的时候从保存的现场开始,继续执行。

看下协程是怎样实现的:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

  • import random

    from time import sleep

    from greenlet import greenlet

    from Queue import Queue

    queue = Queue(1)

    @greenlet

    def procer():

    chars = ['a', 'b', 'c', 'd', 'e']

    global queue

    while True:

    char = random.choice(chars)

    queue.put(char)

    print "Proced: ", char

    sleep(1)

    consumer.switch()

    @greenlet

    def consumer():

    global queue

    while True:

    char = queue.get()

    print "Consumed: ", char

    sleep(1)

    procer.switch()

    if __name__ == "__main__":

    procer.run()

    consumer.run()

  • 应用场景

    我们一直都在大谈协程是什么样一个东西,却从没有提起协程用来干嘛,这个其实大家分析一下就能够知道。从上面的生产者——消费者问题应该能看出,它分别有两个任务,假设交给两个人去执行,但每次只能允许一个人行动。当缓冲区满的时候,生产者是出于等待状态的,这个时候可以将执行任务的权利转交给消费者,当缓冲区空得时候,消费者是出于等待状态的,这个时候可以将执行任务的权利转交给生产者,是不是很容易联想到多任务切换?然后想到线程?最后想到高并发?

    但同学们又会问,既然有了线程为什么还要协程呢?因为线程是系统级别的,在做切换的时候消耗是特别大的,具体为什么这么大等我研究好了再告诉你;同时线程的切换是由CPU决定的,可能你刚好执行到一个地方的时候就要被迫终止,这个时候你需要用各种措施来保证你的数据不出错,所以线程对于数据安全的操作是比较复杂的。而协程是用户级别的切换,且切换是由自己控制,不受外力终止。

    总结

    协程其实模拟了人类活动的一种过程。例如:你准备先写文档,然后修复bug。这时候接到电话说这个bug很严重,必须立即修复(可以看作CPU通知)。于是你暂停写文档,开始去填坑,终于你把坑填完了,你回来写文档,这个时候你肯定是接着之前写的文档继续,难道你要把之前写的给删了,重新写?这就是协程。那如果是子例程呢?那你就必须重新写了,因为退出之后,栈帧就会被弹出销毁,再次调用就是开辟新的栈空间了。

    总结:协程就是用户态下的线程,是人们在有了进程、线程之后仍觉得效率不够,而追求的又一种高并发解决方案。为什么说是用户态,是因为操作系统并不知道它的存在,它是由程序员自己控制、互相协作的让出控制权而不是像进程、线程那样由操作系统调度决定是否让出控制权。

    ㈥ 详解Python中的协程,为什么说它的底层是生成器

    协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的调度和销毁都是程序自己来控制的,因此它更加轻量级也更加灵活。

    协程有这么多优点,自然也会有一些缺点,其中最大的缺点就是需要编程语言自己支持,否则的话需要开发者自己通过一些方法来实现协程。对于大部分语言来说,都不支持这一机制。go语言由于天然支持协程,并且支持得非常好,使得它广受好评,短短几年时间就迅速流行起来。

    对于Python来说,本身就有着一个GIL这个巨大的先天问题。GIL是Python的全局锁,在它的限制下一个Python进程同一时间只能同时执行一个线程,即使是在多核心的机器当中。这就大大影响了Python的性能,尤其是在CPU密集型的工作上。所以为了提升Python的性能,很多开发者想出了使用多进程+协程的方式。一开始是开发者自行实现的,后来在Python3.4的版本当中,官方也收入了这个功能,因此目前可以光明正大地说,Python是支持协程的语言了。

    生成器(generator)

    生成器我们也在之前的文章当中介绍过,为什么我们介绍协程需要用到生成器呢,是因为Python的协程底层就是通过生成器来实现的。

    通过生成器来实现协程的原因也很简单,我们都知道协程需要切换挂起,而生成器当中有一个yield关键字,刚好可以实现这个功能。所以当初那些自己在Python当中开发协程功能的程序员都是通过生成器来实现的,我们想要理解Python当中协程的运用,就必须从最原始的生成器开始。

    生成器我们很熟悉了,本质上就是带有yield这个关键词的函数。

    async,await和future

    从Python3.5版本开始,引入了async,await和future。我们来简单说说它们各自的用途,其中async其实就是@asyncio.coroutine,用途是完全一样的。同样await代替的是yield from,意为等待另外一个协程结束。

    我们用这两个一改,上面的代码就成了:

    async def test(k):

    n = 0

    while n < k:

    await asyncio.sleep(0.5)

    print('n = {}'.format(n))

    n += 1

    由于我们加上了await,所以每次在打印之前都会等待0.5秒。我们把await换成yield from也是一样的,只不过用await更加直观也更加贴合协程的含义。

    Future其实可以看成是一个信号量,我们创建一个全局的future,当一个协程执行完成之后,将结果存入这个future当中。其他的协程可以await future来实现阻塞。我们来看一个例子就明白了:

    future = asyncio.Future()

    async def test(k):

    n = 0

    while n < k:

    await asyncio.sleep(0.5)

    print('n = {}'.format(n))

    n += 1

    future.set_result('success')

    async def log():

    result = await future

    print(result)

    loop = asyncio.get_event_loop()

    loop.run_until_complete(asyncio.wait([

    log(),

    test(5)

    ]))

    loop.close()

    在这个例子当中我们创建了两个协程,第一个协程是每隔0.5秒print一个数字,在print完成之后把success写入到future当中。第二个协程就是等待future当中的数据,之后print出来。

    在loop当中我们要调度执行的不再是一个协程对象了而是两个,所以我们用asyncio当中的wait将这两个对象包起来。只有当wait当中的两个对象执行结束,wait才会结束。loop等待的是wait的结束,而wait等待的是传入其中的协程的结束,这就形成了一个依赖循环,等价于这两个协程对象结束,loop才会结束。

    总结

    async并不只是可以用在函数上,事实上还有很多其他的用法,比如用在with语句上,用在for循环上等等。这些用法比较小众,细节也很多,就不一一展开了,大家感兴趣的可以自行去了解一下。

    不知道大家在读这篇文章的过程当中有没有觉得有些费劲,如果有的话,其实是很正常的。原因也很简单,因为Python原生是不支持协程这个概念的,所以在一开始设计的时候也没有做这方面的准备,是后来觉得有必要才加入的。那么作为后面加入的内容,必然会对原先的很多内容产生影响,尤其是协程借助了之前生成器的概念来实现的,那么必然会有很多耦合不清楚的情况。这也是这一块的语法很乱,对初学者不友好的原因。

    ㈦ Python异步编程4:协程函数,协程对象,await关键字

    协程函数:async def 函数名。3.5+

    协程对象:执行协程函数()得到的协程对象。

        3.5之后的写法:

        3.7之后的写法:更简便

    await后面 跟 可等待的对象。(协程对象,Future,Task对象 约等于IO等待)

    await实例2:串行执行。 一个协程函数里面可以支持多个await ,虽然会串行,但是如果有其他协程函数,任务列表也在执行,依然会切换。只是案例中的main对应执行的others1和others2串行 。 await会等待对象的值得到之后才继续往下走。

    ㈧ python后端开发需要学什么

    第一阶段:Python语言基础


    主要学习Python最基础知识,如Python3、数据类型、字符串、函数、类、文件操作等。阶段课程结束后,学员需要完成Pygame实战飞机大战、2048等项目。


    第二阶段:Python语言高级


    主要学习Python库、正则表达式、进程线程、爬虫、遍历以及MySQL数据库


    第三阶段:Pythonweb开发


    主要学习HTML、CSS、JavaScript、jQuery等前端知识,掌握python三大后端框架(Django、 Flask以及Tornado)。需要完成网页界面设计实战;能独立开发网站。


    第四阶段:Linux基础


    主要学习Linux相关的各种命令,如文件处理命令、压缩解压命令、权限管理以及Linux Shell开发等。


    第五阶段:Linux运维自动化开发


    主要学习Python开发Linux运维、Linux运维报警工具开发、Linux运维报警安全审计开发、Linux业务质量报表工具开发、Kali安全检测工具检测以及Kali 密码破解实战。


    第六阶段:Python爬虫


    主要学习python爬虫技术,掌握多线程爬虫技术,分布式爬虫技术。


    第七阶段:Python数据分析和大数据


    主要学习numpy数据处理、pandas数据分析、matplotlib数据可视化、scipy数据统计分析以及python 金融数据分析;Hadoop HDFS、python Hadoop MapRece、python Spark core、python Spark SQL以及python Spark MLlib。


    第八阶段:Python机器学习


    主要学习KNN算法、线性回归、逻辑斯蒂回归算法、决策树算法、朴素贝叶斯算法、支持向量机以及聚类k-means算法。


    关于python后端开发需要学什么的内容,青藤小编就和您分享到这里了。如果您对python编程有浓厚的兴趣,希望这篇文章可以为您提供帮助。如果您还想了解更多关于python编程的技巧及素材等内容,可以点击本站的其他文章进行学习。

    ㈨ python协程(4):asyncio

    asyncio是官方提供的协程的类库,从python3.4开始支持该模块

    async & awiat是python3.5中引入的关键字,使用async关键字可以将一个函数定义为协程函数,使用awiat关键字可以在遇到IO的时候挂起当前协程(也就是任务),去执行其他协程。
    await + 可等待的对象(协程对象、Future对象、Task对象 -> IO等待)
    注意:在python3.4中是通过asyncio装饰器定义协程,在python3.8中已经移除了asyncio装饰器。

    事件循环,可以把他当做是一个while循环,这个while循环在周期性的运行并执行一些协程(任务),在特定条件下终止循环。
    loop = asyncio.get_event_loop():生成一个事件循环
    loop.run_until_complete(任务):将任务放到事件循环

    Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task() 函数以外,还可以用低层级的 loop.create_task() 或 ensure_future() 函数。不建议手动实例化 Task 对象。

    本质上是将协程对象封装成task对象,并将协程立即加入事件循环,同时追踪协程的状态。

    注意:asyncio.create_task() 函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用 asyncio.ensure_future() 函数。

    下面结合async & awiat、事件循环和Task看一个示例
    示例一:

    *注意:python 3.7以后增加了asyncio.run(协程对象),效果等同于loop = asyncio.get_event_loop(),loop.run_until_complete(协程对象) *

    示例二:

    注意:asyncio.wait 源码内部会对列表中的每个协程执行ensure_future从而封装为Task对象,所以在和wait配合使用时task_list的值为[func(),func()] 也是可以的。

    示例三:

    ㈩ Python怎么多线程中添加协程

    由于python是一种解释性脚本语言,python的多线程在运行过程中始终存在全局线程锁。
    简单的来说就是在实际的运行过程中,python只能利用一个线程,因此python的多线程并不达到C语言多线程的性能。
    可以使用多进程来代替多线程,但需要注意的是多进程最好不要涉及到例如文件操作的频繁操作IO的功能。

    热点内容
    c盘删除缓存文件对系统有影响吗 发布:2024-05-17 04:27:47 浏览:339
    python不能输入中文 发布:2024-05-17 04:27:34 浏览:110
    小米电视密码设置在哪里 发布:2024-05-17 04:06:12 浏览:426
    java正则sql 发布:2024-05-17 04:05:52 浏览:620
    51单片机c语言教程郭天祥 发布:2024-05-17 03:46:13 浏览:890
    c语言程序特点 发布:2024-05-17 03:41:13 浏览:461
    2014二级c语言考试大纲 发布:2024-05-17 03:23:20 浏览:418
    到店系统源码 发布:2024-05-17 03:18:34 浏览:758
    解编程解密 发布:2024-05-17 02:57:26 浏览:48
    c语言掷骰子 发布:2024-05-17 02:11:05 浏览:489