Python并发编程:协程介绍

一 引子

1

2

3

基于单线程来实现并发,即只用一个主线程(很明显可利用的CPU只有一个)情况下实现并发,先回顾一下并发的本质:切换+保存状态

CPU正在运行一个任务,会在两种情况下切走去执行其它的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长或有一个优先级更高的程序替代了它

  

1

ps:在介绍进程理论时,提及进程的三种执行状态,而线程才是执行单位,所以也可以将上图理解为线程的三种状态

  

  一:其中第二种情况并不能提升效率,只是让CPU能够雨露均沾,实现看起来所有任务都被“同时”执行的效果,如果多个任务都是纯计算的,这种切换反而会降低效率。为此我们可以基于yield来验证。yield本身就是一种在单线程下可以保存任务运行状态的方法,我们来简单复习一下:

1

2

1、yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级

2、send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换

  

单纯地切换反而会降低运行效率

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# 串行执行

import time

def consumer(res):

    '''任务1: 接收数据,处理数据'''

    pass

def producer():

    '''任务2:生成数据'''

    res = []

    for in range(100):

        res.append(i)

    return res

start = time.time()

# 串行执行

res = producer()

consumer(res)       # 写成consumer(producer())会降低执行效率

stop = time.time()

print(stop-start)   # 1.835823059082031

  

并发执行

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

# 基于yield并发执行

import time

def consumer():

    '''任务1:接收数据,处理数据'''

    while True:

        = yield

def producer():

    '''任务2:生产数据'''

    = consumer()

    next(g)

    for in range(100):

        g.send(i)

start = time.time()

# 基于yield保存状态,实现两个任务直接来回切换,即并发的效果

# ps:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的

producer()

stop = time.time()

print(stop-start)  # 2.503395080566406

  二: 第一种情况的切换。在任务一遇到io情况下,切到任务二去执行,这样就可以利用任务一阻塞的时间完成任务二的计算,效率的提示就在于此

yield并不能实现遇到io切换

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import time

def consumer():

    '''任务1:接收数据,处理数据'''

    while True:

        = yield

def producer():

    '''任务2:生产数据'''

    = consumer()

    next(g)

    for in range(100):

        g.send(i)

        time.sleep(2)

start = time.time()

producer()  # 并发执行,但是任务producer遇到io就会阻塞住,并不会切换到该线程内的其它任务去执行

stop = time.time()

producer(stop-start)

  

  对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被CPU执行的状态,相当于我们在用户程序级别将自己的io操作最大限度的隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将CPU的执行权限分配给我们的线程

1

协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案

  

1

2

3

1、可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续运行

2、作为1的补充,可以检测io操作,在遇到io操作的情况下才发生切换

  

二  协程介绍

协程:是单线程下的并发,又称微线程,纤程

一句话说明说明是协程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的

需要强调的是:

1

2

3

1、Python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出CPU执行权限,切换其它线程运行)

2、单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非io操作的切换与效率无关)

  对比操作系统控制线程的切换,用户在单线程内控制协程的切换

优点如下:

1

2

1、协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

2、单线程内就可以实现并发的效果,最大限度地利用CPU

  

缺点如下:

1

2

3

1、协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个进程内开启多个线程,每个线程内开启协程

2、协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

  

总结协程特点:

1. 必须在只有一个单线程里实现并发

2. 修改共享数据不需要加锁

3. 用户程序里自己保存多个控制刘的上下文栈

4. 附加:一个协程遇到io操作自动切换到其它协程(如何实现检测io,yield,greenlet都无法实现,就用到了gevent模块(select机制))

# 基于yield并发执行
import timedef consumer():'''任务1:接收数据,处理数据'''
while True:x = yielddef producer():'''任务2:生产数据'''
g = consumer()next(g)for i in range(100):g.send(i)start = time.time()
# 基于yield保存状态,实现两个任务直接来回切换,即并发的效果
# ps:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的
producer()stop = time.time()
print(stop-start)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/508577.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IOC中Bean的生命周期

生命周期的各个阶段: 可以分为三个阶段:产生-使用-销毁 又可以分四个阶段:四个阶段 实例化 ->属性注入->初始化 ->销毁 实例化后到使用的初始化过程: 属性赋值 ->处理各种Aware接口->实现BeanPostProcessor的b…

【大厂AI课学习笔记NO.66】TensorFlow

TensorFlow 这个框架,实在是太有名了,最近周红衣都在大力的宣传和讲解。 他说的是对的,人工智能,就是大力出奇迹,就是大量的算力,大量的数据,加上模型的加持,实现的智能感觉。 Goog…

《数字图像处理(MATLAB版)》相关算法代码及其分析(1)

目录 1 自适应中值滤波算法 1.1 函数定义 1.2 输入参数检查 1.3 初始化 1.4 自适应中值滤波过程 1.5 处理剩余未处理的像素 1.6 总结 2 计算输入数组的平均值 2.1 函数定义 2.2 注释 2.3 输入验证 2.4 计算平均值 2.5 总结 3 基于高斯模型的贝叶斯分类器 3.1 函…

有趣的CSS - 一串乱码

大家好,我是 Just,这里是「设计师工作日常」,今天分享的是通过 css 来实现一段不停变化的 bug 乱码效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面…

异常网络下TCP的可靠服务机制(慢启动、拥塞避免、快重传、快恢复)

目录 TCP超时重传拥塞控制概述慢启动和拥塞避免下面讲解发送端如何判断拥塞发生。 快速重传和快速恢复 本文描述TCP在异常网络下的处理方式 以保证其可靠的数据传输的服务 TCP超时重传 tcp服务能够重传其超时时间内没有收到确认的TCP报文段,tcp模块为每一个报文段都…

加密与安全_探索数字证书

文章目录 Pre概述使用keytool生成证书使用Openssl生成证书 (推荐)证书的吊销小结 Pre PKI - 借助Nginx 实现Https 服务端单向认证、服务端客户端双向认证 PKI - 04 证书授权颁发机构(CA) & 数字证书 PKI - 数字签名与数字证…

mysql8修改密码

mysql8.0修改密码 windows下忘了MySQL8.0的密码,可以通过以下方式修改。 1、管理员方式打开cmd命令窗口 输入: net stop mysql接着输入: mysqld --console --skip-grant-tables --shared-memory2、管理员方式打开另外一个cmd窗口 输入&…

电脑要用多少V的电源?电脑电源输入电压是市电

台式电源的输出电压是多少? 电脑电源输出一般有三种不同的电压,分别是: 12V、5V、3.3V。 电脑电源负责给电脑配件供电,如CPU、主板、内存条、硬盘、显卡等,是电脑的重要组成部分。 工作电流根据不同的硬件及其使用状…

数据结构c版(2)——二叉树

本章我们来了解一下二叉树这一概念。 目录 1.树概念及结构 1.1树的概念​​​​​​​ 1.2 树的特点: 1.3 树的相关概念 1.4 树的表示​​​​​​​ 1.5 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构 2.1概念 …

HTML+CSS:花式加载

效果演示 实现了一个动态加载文本效果&#xff0c;通过定义变量和应用动画效果来实现文本的动态展示。 Code <div class"container"><h1>loading...</h1> </div>:root {--text-color: orangered; /* 定义文本颜色变量为橙红色 */--inner-st…

设计模式(十二)享元模式

请直接看原文: 原文链接:设计模式&#xff08;十二&#xff09;享元模式-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 享元模式定义 享元模式是结构型设计模式的一种&am…

java 企业培训管理系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 企业培训管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…