• 感谢您的阅读。喜欢的、有用的就请大哥大嫂们高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力以及转载收藏动力。欢迎转载!
  • 版权声明:本文原创发表于 【请点击连接前往】 ,未经作者同意必须保留此段声明!如有侵权请联系我删帖处理!
  • 我的博客:http://www.cnblogs.com/GJM6/  -  传送门:【点击前往
 
 

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

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

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

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

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

协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多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 ,所有和用户交互的程序都会有这个问题。

GJM : 进程、线程和协程的理解的更多相关文章

  1. python进程.线程和协程的总结

    I.进程: II.多线程threading总结 threading用于提供线程相关的操作,线程是应用系统中工作的最小单位(cpu调用的最小单位). Python当前版本的多线程没有实现优先级,线程组, ...

  2. python之进程,线程,协程简单理解

    进程:资源单位,由操作系统控制调度.正在执行的一个程序或者过程,进程之间不共享资源,进程间通讯手段:管道,队列,信号量等.多用于计算密集型场景,如金融计算 线程:是cpu的最小执行单位,由操作系统控制 ...

  3. python系列7进程线程和协程

    目录 进程 线程 协程  上下文切换 前言:线程和进程的关系图 由下图可知,在每个应用程序执行的过程中,都会去产生一个主进程和主线程来完成工作,当我们需要并发的执行的时候,就会通过主进程去生成一系列的 ...

  4. Python(八)进程、线程、协程篇

    本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...

  5. python中socket、进程、线程、协程、池的创建方式和应用场景

    进程 场景 利用多核.高计算型的程序.启动数量有限 进程是计算机中最小的资源分配单位 进程和线程是包含关系 每个进程中都至少有一条线程 可以利用多核,数据隔离 创建 销毁 切换 时间开销都比较大 随着 ...

  6. python 进程、线程与协程的区别

    进程.线程与协程区别总结 - 1.进程是计算器最小资源分配单位 - 2.线程是CPU调度的最小单位 - 3.进程切换需要的资源很最大,效率很低 - 4.线程切换需要的资源一般,效率一般(当然了在不考虑 ...

  7. 进程、线程、协程和GIL(二)

    上一篇博客讲了进程.线程.协程和GIL的基本概念,这篇我们来说说在以下三点: 1> python中使用threading库来创建线程的两种方式 2> 使用Event对消来判断线程是否已启动 ...

  8. 图解Python 【第八篇】:网络编程-进程、线程和协程

    本节内容一览图: 本章内容: 同步和异步 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 一.同步和异步 你叫我去吃饭 ...

  9. 进程、线程、协程的基本解析(python代码)

    进程什么是进程?程序就是一堆放在磁盘上的代码,进程是一段程序的运行过程正规点说,进程一般由程序.数据集.进程控制块三部分组成 什么进程切换?进程切换是,一个正在运行的进程被中断,操作系统指定另一个进程 ...

随机推荐

  1. 每天一个linux命令(44):top命令

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法.top是一个动态显示过程,即可以通过用户按键来不断刷新 ...

  2. require.js笔记

    笔记参考来源:阮一峰  http://www.ruanyifeng.com/blog/2012/10/javascript_module.html   1. 浏览器端的模块只能采用“异步加载”方式 = ...

  3. Mina、Netty、Twisted一起学(十):线程模型

    要想开发一个高性能的TCP服务器,熟悉所使用框架的线程模型非常重要.MINA.Netty.Twisted本身都是高性能的网络框架,如果再搭配上高效率的代码,才能实现一个高大上的服务器.但是如果不了解它 ...

  4. AngularJs单元测试

    这篇文章主要介绍了angularJS中的单元测试实例,本文主要介绍利用Karma和Jasmine来进行ng模块的单元测试,并用Istanbul  来生成代码覆盖率测试报告,需要的朋友们可以参考下,以下 ...

  5. JIRA简介

    JIRA是Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪.客户服务.需求收集.流程审批.任务跟踪.项目跟踪和敏捷管理等工作领域,其配置灵活.功能全面.部署简单.扩展丰富.“Jir ...

  6. UNIX/Linux打包,压缩 ,解压:

    1.tar打包命令 ①tar cvf tarname.tar file1 file2 $ .log $ .log $ .log $ tar cvf alllog.tar *.log a .log 0K ...

  7. 自制Https证书并在Spring Boot和Nginx中使用

    白话Https一文中, 介绍了Https存在的目的和工作原理,但多是偏向于原理性的介绍,本文介绍如何一步一步自制一个能够通过浏览器认证的Https证书,并讲解在Spring Boot环境和Nginx环 ...

  8. JavaScript之旅(三)

    JavaScript之旅(三) 三.函数 在JavaScript中,定义函数的方式如下: function abs(x) { ... return ...; } 如果没有return,返回结果为und ...

  9. web框架--flask

    flask介绍 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求 ...

  10. C# 可空值类型

    一个值类型永远不可能为null,但是当数据库中的某列数据允许为空时,或者另一种语言中的数据类型(引用类型)对应C#的是值类型,当需要和另外的语言交互时,就有可能需要处理空值的问题. 所以,CLR中引用 ...