协程,又称微线程,纤程。英文名Coroutine。

协程是啥

协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

协程和线程差异

在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。

简单实现协程

import time

def test1():
while True:
print("--test1--")
yield
time.sleep(0.5) def test2():
while True:
print("--test2--")
yield
time.sleep(0.5) if __name__ == "__main__":
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)

greenlet

安装方式

sudo pip3 install greenlet
import time
from greenlet import greenlet def test1():
while True:
print("--test1--")
g2.switch()
time.sleep(0.5) def test2():
while True:
print("--test2--")
g1.switch()
time.sleep(0.5) g1 = greenlet(test1)
g2 = greenlet(test2) g1.switch()

运行效果:

--test1--
--test2--
--test1--
--test2--
--test1--
--test2--
--test1--
--test2--
--test1--
。。。

gevent

greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent

其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。

由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

安装

pip3 install gevent

1. gevent的使用

import gevent

def test(n):
for i in range(n):
print(gevent.getcurrent(), i) g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5) g1.join()
g2.join()
g3.join()

2. gevent切换执行

import gevent

def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0.5) g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5) g1.join()
g2.join()
g3.join()

运行结果:

<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f04bfffb748: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f04bfffb948: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f04bfffba48: test(5)> 4

3. 给程序打补丁

import time
import gevent def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5) g1 = gevent.spawn(test, 5)
g2 = gevent.spawn(test, 5)
g3 = gevent.spawn(test, 5) g1.join()
g2.join()
g3.join()

运行结果:

<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f1a8dde6748: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f1a8dde6948: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f1a8dde6a48: test(5)> 4
import time
import gevent
from gevent import monkey monkey.patch_all() # 讲程序中用到的耗时的代码,换为gevent中实现的代码 def test(n):
for i in range(n):
print(gevent.getcurrent(), i)
time.sleep(0.5) # g1 = gevent.spawn(test, 5)
# g2 = gevent.spawn(test, 5)
# g3 = gevent.spawn(test, 5) # g1.join()
# g2.join()
# g3.join() gevent.joinall([
gevent.spawn(test, 5),
gevent.spawn(test, 5),
gevent.spawn(test, 5)
])

运行结果:

<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 0
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 0
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 0
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 1
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 1
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 1
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 2
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 2
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 2
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 3
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 3
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 3
<Greenlet "Greenlet-0" at 0x7f7f96b53648: test(5)> 4
<Greenlet "Greenlet-1" at 0x7f7f96b53748: test(5)> 4
<Greenlet "Greenlet-2" at 0x7f7f96b53848: test(5)> 4

进程、线程、协程对比

请仔细理解如下的通俗描述

  • 有一个老板想要开个工厂进行生产某件商品(例如剪子)
  • 他需要花一些财力物力制作一条生产线,这个生产线上有很多的器件以及材料这些所有的 为了能够生产剪子而准备的资源称之为:进程
  • 只有生产线是不能够进行生产的,所以老板的找个工人来进行生产,这个工人能够利用这些材料最终一步步的将剪子做出来,这个来做事情的工人称之为:线程
  • 这个老板为了提高生产率,想到3种办法:
    1. 在这条生产线上多招些工人,一起来做剪子,这样效率是成倍増长,即单进程 多线程方式
    2. 老板发现这条生产线上的工人不是越多越好,因为一条生产线的资源以及材料毕竟有限,所以老板又花了些财力物力购置了另外一条生产线,然后再招些工人这样效率又再一步提高了,即多进程 多线程方式
    3. 老板发现,现在已经有了很多条生产线,并且每条生产线上已经有很多工人了(即程序是多进程的,每个进程中又有多个线程),为了再次提高效率,老板想了个损招,规定:如果某个员工在上班时临时没事或者再等待某些条件(比如等待另一个工人生产完谋道工序 之后他才能再次工作) ,那么这个员工就利用这个时间去做其它的事情,那么也就是说:如果一个线程等待某些条件,可以充分利用这个时间去做其它事情,其实这就是:协程方式

简单总结

  1. 进程是资源分配的单位
  2. 线程是操作系统调度的单位
  3. 进程切换需要的资源很最大,效率很低
  4. 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
  5. 协程切换任务资源很小,效率高
  6. 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发

python中的协程的更多相关文章

  1. python中的协程及实现

    1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...

  2. python中的协程:greenlet和gevent

    python中的协程:greenlet和gevent 协程是一中多任务实现方式,它不需要多个进程或线程就可以实现多任务. 1.通过yield实现协程: 代码: import time def A(): ...

  3. Python中异步协程的使用方法介绍

    1. 前言 在执行一些 IO 密集型任务的时候,程序常常会因为等待 IO 而阻塞.比如在网络爬虫中,如果我们使用 requests 库来进行请求的话,如果网站响应速度过慢,程序一直在等待网站响应,最后 ...

  4. Python中Paramiko协程方式详解

    什么是协程 协程我们可以看做是一种用户空间的线程. 操作系统对齐存在一无所知,需要用户自己去调度. 比如说进程,线程操作系统都是知道它们存在的.协程的话是用户空间的线程,操作系统是不知道的. 为什么要 ...

  5. 协程及Python中的协程

    1 协程 1.1协程的概念 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程.(其实并没有说明白~) 我觉得单说协程,比较抽象,如果对线程有一定了解 ...

  6. python中多进程+协程的使用以及为什么要用它

    前面讲了为什么python里推荐用多进程而不是多线程,但是多进程也有其自己的限制:相比线程更加笨重.切换耗时更长,并且在python的多进程下,进程数量不推荐超过CPU核心数(一个进程只有一个GIL, ...

  7. Python | 详解Python中的协程,为什么说它的底层是生成器?

    今天是Python专题的第26篇文章,我们来聊聊Python当中的协程. 我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下.协程又称为是微线程, ...

  8. Python中的协程,为什么说它的底层是生成器?

    我们曾经在golang关于goroutine的文章当中简单介绍过 协程 的概念,我们再来简单review一下.协程又称为是微线程,英文名是Coroutine.它和线程一样可以调度,但是不同的是线程的启 ...

  9. python中的协程并发

    python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...

随机推荐

  1. python 上传文件

    上周产品给我提了个需求,大体是做一个后台系统,管理游戏比赛落地页的数据更新,难点在于需要给CDN上传文件.现在把经验记录下来,下次有类似的需求能提高开发效率. 我使用的是网宿CDN,没有用网宿的SDK ...

  2. SQL Server 自动循环归档分区数据脚本

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/表分区 概述 在很多业务场景下我们需要对一些记录量比较大的表进行分区,同时为了保证性能需要将一些旧的数据进行归档.在分区表很多的情 ...

  3. java处理数据库不支持的emoji表情符

    一般数据库的编码是utf8,utf8是不支持存储表情符的,当存入的微信昵称带有表情符时就会出现乱码情况,有两种解决方法: 1.mysql数据库升级到5.5版本以上,utf8改为utf8mb4,utf8 ...

  4. 解决Django项目数据库无法迁移问题

    找到自己的虚拟环境,以下是我自己的环境路径 D:\xunihuanjing\venv\Lib\site-packages\django\contrib\admin\migrations 然后删除里面的 ...

  5. 线程池工厂Executors编程的艺术

    Executors是一个线程池的工厂类,提供各种有用的线程池的创建,使用得当,将会使我们并发编程变得简单!今天就来聊聊这个工厂类的艺术吧! Executors只是Executor框架的主要成员组件之一 ...

  6. vim常用命令行备忘总结

    一 窗口切换 1 :sp    水平切换当前窗口 2 :vsp 垂直切换当前窗口 3 :clo 关闭活动窗口 4 : on 只保留活动窗口 5 : ctrl + w  在窗口间循环切换  ctrl + ...

  7. Shell中for循环的几个常用写法

    第一类:数字性循环-----------------------------for1-1.sh #!/bin/bash ;i<=;i++)); do echo $(expr $i \* + ); ...

  8. SVN切换账号

    问题背景 SVN账号在登录的时候,默认是保存在个人电脑的 C:\Users\Administrator\AppData\Roaming\Subversion\auth\svn.simple\ 目录下的 ...

  9. 程序员周末阿里面试,5分钟就被一道题秒杀:HashMap与Hashtable

    你们可能会想,我这么菜的吗?5分钟都坚持不了? 本文说起来会有点尴尬,毕竟这是我曾经经历过的故事 那时候的我还真菜,每天写着 if/ for 及一些简单的业务逻辑代码,虽工作有些日子了,但技术水平还停 ...

  10. mysql 开发基础系列8 表的存储引擎

    一. 表的存储引擎 1. 概述 插件式存储引擎是mysql数据库最重要的特性之一, 用户可以根据应用的需要选择如何存储和索引数据,是否使用事务等.在mysql 5.0里支持的引擎包括: MyISAM, ...