进程、线程和协程的理解

2023-11-05

进程、线程和协程的理解

进程线程协程之间的关系和区别也困扰我一阵子了,最近有一些心得,写一下。

进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。

线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。

协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

进程和其他两个的区别还是很明显的。

协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。

打个比方吧,假设有一个操作系统,是单核的,系统上没有其他的程序需要运行,有两个线程 A 和 B ,A 和 B 在单独运行时都需要 10 秒来完成自己的任务,而且任务都是运算操作,A B 之间也没有竞争和共享数据的问题。现在 A B 两个线程并行,操作系统会不停的在 A B 两个线程之间切换,达到一种伪并行的效果,假设切换的频率是每秒一次,切换的成本是 0.1 秒(主要是栈切换),总共需要 20 + 19 * 0.1 = 21.9 秒。如果使用协程的方式,可以先运行协程 A ,A 结束的时候让位给协程 B ,只发生一次切换,总时间是 20 + 1 * 0.1 = 20.1 秒。如果系统是双核的,而且线程是标准线程,那么 A B 两个线程就可以真并行,总时间只需要 10 秒,而协程的方案仍然需要 20.1 秒。

一个实际一点的例子:thread.py

    #!/usr/bin/python
    # python thread.py
    # python -m gevent.monkey thread.py

    import threading

    class Thread(threading.Thread):

        def __init__(self, name):
            threading.Thread.__init__(self)
            self.name = name

        def run(self):
            for i in xrange(10):
                print self.name

    threadA = Thread("A")
    threadB = Thread("B")

    threadA.start()
    threadB.start()
    

运行:

python thread.py

如果你的输出是均匀的:

A
B
A
B
...

那么总共发生了 20 次切换:主线程 -> A -> B -> A -> B …

再看一个协程的例子:gr.py

    #!/usr/bin/python
    # python gr.py

    import greenlet

    def run(name, nextGreenlets):
        for i in xrange(10):
            print name
        if nextGreenlets:
            nextGreenlets.pop(0).switch(chr(ord(name) + 1), nextGreenlets)

    greenletA = greenlet.greenlet(run)
    greenletB = greenlet.greenlet(run)

    greenletA.switch('A', [greenletB])
    

greenlet 是 Python 的协程实现。

运行:

python gr.py

此时发生了 2 次切换:主协程 -> A -> B

可能你已经注意到了,还有一个命令:

python -m gevent.monkey thread.py

gevent 是基于 greenlet 的一个 python 库,它可以把 python 的内置线程用 greenlet 包装,这样在我们使用线程的时候,实际上使用的是协程,在上一个协程的例子里,协程 A 结束时,由协程 A 让位给协程 B ,而在 gevent 里,所有需要让位的协程都让位给主协程,由主协程决定运行哪一个协程,gevent 也会包装一些可能需要阻塞的方法,比如 sleep ,比如读 socket ,比如等待锁,等等,在这些方法里会自动让位给主协程,而不是由程序员显示让位,这样程序员就可以按照线程的模式进行线性编程,不需要考虑切换的逻辑。

gevent 版的命令发生了 3 次切换:主协程 -> A -> 主协程 -> B

假设代码质量相同,用原生的协程实现需要切换 n 次,用协程包装后的线程实现,就需要 2n - 1 次,姑且算是两倍吧。很显然,单纯从效率上来说,代码质量相同的前提下,用 gevent 永远也不可能比用 greenlet 快,然而,问题往往不那么单纯,比方说,单纯从效率上来说,代码质量相同的前提下,用 C 实现的程序永远不可能比汇编快。

再来说说 python 的线程,python 的线程不是标准线程,在 python 中,一个进程内的多个线程只能使用一个 CPU 。

重新来看一下协程和线程的区别:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。

如果使用 gevent 包装后的线程,程序员就不必承担调度的责任,而 python 的线程本身就没有使用多 CPU 的能力,那么,用 gevent 包装后的线程,取代 python 的内置线程,不是只有避免无意义的调度,提高性能的好处,而没有什么坏处了吗?

答案是否定的。举一个例子,有一个 GUI 程序,上面有两个按钮,一个 运算 一个 取消 ,点击运算,会有一个运算线程启动,不停的运算,点击取消,会取消这个线程,如果使用 python 的内置线程或者标准线程,都是没有问题的,即便运算线程不停的运算,调度器仍然会给 GUI 线程分配时间片,用户可以点击取消,然而,如果使用 gevent 包装后的线程就完蛋了,一旦运算开始,GUI 就会失去相应,因为那个运算线程(协程)霸着 CPU 不让位。不单是 GUI ,所有和用户交互的程序都会有这个问题。


本文转自http://blog.leiqin.name/2012/12/02/%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%8D%8F%E7%A8

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程、线程和协程的理解 的相关文章

随机推荐

  • HttpClient调用restful接口

    try HttpClient client HttpClients createDefault HttpPost request2 new HttpPost ip List
  • C语言笔记第01章:初识C语言

    第01章 初识C语言 来自 中南林业科技大学软件协会学术部 谢添 时间 2022 10 05 官网 https www csuftsap cn 本章所有提供代码均已测试 读万卷书不如行万里路 一定要把代码都自己敲一遍并测试 一件事 你若不想
  • lcd硬件原理

    知识点参考百问网开发手册和正点原子驱动开发手册 1 LCD硬件工作原理 LCD屏幕是由一个个像素点组成 上述图片中每一个黑点都对应一个像素点 像素点就类似一个灯 在 OLED 显示器中 像素点就是一个小灯 这个小灯是 RGB 灯 也就是由
  • 线程池代码的C实现以及在各种开源框架中的使用

    前言 1 池化技术 简单点来说 就是提前保存大量的资源 以备不时之需 对于线程 内存 oracle的连接对象等等 这些都是资源 程序中当你创建一个线程或者在堆上申请一块内存时 都涉及到很多系统调用 也是非常消耗CPU的 如果你的程序需要很多
  • eclipse 如何创建一个Dynamic Web project (动态web项目)

    1 准备工作 eclipse的下载安装 2 创建Dynamic Web project 至此一个Dynamic web project生成完毕 项目结构为
  • Docker部署笔记

    纯手记 无解释 莫抄 目录 安装docker 启动所有容器 关闭所有容器 删除所有容器 自定义容器IP 复制 Jenkins 其他参考 Document mysql redis nginx Rabbitmq elasticsearch lo
  • 科研初体验之Linux服务器的入门使用,关于分配了Linux账号之后怎么用,以及怎么利用Linux服务器来跑我们的python代码

    前情提要 如果有人看了我之前发的乱七八糟的博客的话 应该就能了解到 我之前是计算机专业大三的学生 好不容易get到了保研的名额 前段时间就一直在操练LeetCode 到处报夏令营啊 预推免什么的 最后喜提中科院计算所的offer 我之前都不
  • 通用业务平台设计(四):灰度发布架构升级

    强烈推荐一个大神的人工智能的教程 http www captainai net zhanghan 前言 在上家公司 随着业务的不断拓展 从支持单个国家单个主体演变成支持多个国家多个主体 在演化的过程中沉淀出平台 短信 活体 push等 能力
  • LabVIEW2020编程基础:Database Toolkit 创建数据库表及字段

    LabVIEW2020编程基础 Database Toolkit数据库系列教程 1 LabVIEW2020编程基础 Database Toolkit 创建数据库表及字段 2 LabVIEW2020编程基础 Database Toolkit
  • MySQL - 各种超时时间 - 学习与探究

    1 应用场景 主要用于学习与探究MySQL各种超时时间 应用在合适的场景下 2 学习 操作 1 文档阅读 https wen geekr dev chatgpt 官方文档 其他资料 2 整理输出 2 1 是什么 MySQL中有多个超时时间
  • java自动化测试语言高级之Object 类

    java自动化测试语言高级之Object 类 文章目录 java自动化测试语言高级之Object 类 Java Object 类 Java Object 类 Java Object 类是所有类的父类 也就是说 Java 的所有类都继承了 O
  • vue项目 高德地图实现区域多个标点并通过半径距离以此点绘制多个圆(circle),动态显示隐藏圆;实现根据经纬度获取中文地址,根据地址获取经纬度;地图控件显示隐藏

    最终效果 一 需求 最近公司有这样一个需求 指定一个区域根据一个距离测算需要开放多少个门店才能覆盖整个指定区域 暂不考虑人口密集 山区等因素 大概估算 因此稍微了解了一下 高德地图的API 记录一下常用高德地图进行定位 标点 自定义标点 测
  • 优美的小程序启动页(附源码)

    优美的小程序启动页 附源码 1 看效果 2 注意点 实现这一效果其实是很简单的 首先我们要把自己设置的启动页的路径写在app jon中 注意小程序默认第一个路径是小程序加载的开始页 其次我们的页面有时会出现这种情况 这是应为在x json文
  • Mysql 将逗号隔开的属性字段数据由列转行

    Mysql 将逗号隔开的属性字段转行为行数据 Mysql 将逗号隔开的属性字段转行为行数据 场景 在开发时 我们会根据需求进行数据库表的设计 有时我们在设计数据表时无法很好的符合三大范式 原因场景的复杂性 假如时时刻刻遵顼三大范式 会增加我
  • 搭建私有YUM仓库_及_内网镜像站

    搭建私有YUM仓库 及 内网镜像站 搭建私有YUM仓库 自己定制的rpm包 私有yum仓库环境系统版本 centos7 4 IP 192 168 1 47 最好能上公网 私有yum仓库服务端配置 第一 创建使用yum仓库存放路径 mkdir
  • 爱心循环java代码

    package 对阳子心动 public class aixindaima public static void main String args throws InterruptedException int count 0 for fl
  • 函数、对象在内存中存在形式

    一 php底层内存分区 php将内存分为5个区 堆区一般存对象 栈区一般存基本数据类型 普通变量 和函数 全局区存全局变量和静态变量 常量区存常量 代码区存代码 二 函数调用时栈区变化 这是一个简单的递归函数示例 当主函数调用counts函
  • 服务器IP装系统,服务器 安装系统 自动设置ip

    服务器 安装系统 自动设置ip 内容精选 换一换 在移动设备上正确安装APP后 就可以通过APP登录NetEco服务器 在裸金属服务器发放过程中 普通裸金属服务器的操作系统需要从云端下载 安装 下载过程会消耗较长时间 基于云硬盘的裸金属服务
  • python 画正弦曲线

    要画正弦曲线先设定一下x的取值范围 从0到2 要用到numpy模块 numpy pi 表示 numpy arange 0 2 0 01 从0到2 以0 01步进 令 x numpy arange 0 2 numpy pi 0 01 y nu
  • 进程、线程和协程的理解

    进程 线程和协程的理解 进程 线程和协程之间的关系和区别也困扰我一阵子了 最近有一些心得 写一下 进程拥有自己独立的堆和栈 既不共享堆 亦不共享栈 进程由操作系统调度 线程拥有自己独立的栈和共享的堆 共享堆 不共享栈 线程亦由操作系统调度