进程、线程和协程的理解

转自

http://blog.leiqin.info/2012/12/02/%E8%BF%9B%E7%A8%8B-%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%8D%8F%E7%A8%8B%E7%9A%84%E7%90%86%E8%A7%A3.html

进程、线程和协程的理解

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

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

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

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

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

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

时间: 2024-10-22 13:38:22

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

进程、线程和协程的理解-自己随笔

1. IO 操作不占用CPU(从硬盘读数据,从网络读数据,从内存读取数据) 计算占用CPU,例如1+1=2的计算就是占用CPU的. python 多线程,不适合CPU密集操作系统的任务,适合IO操作密集型的任务. 2. 进程.线程和协程之间的关系和区别也困扰我一阵子了,最近有一些心得,写一下. 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度. 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的). 协程和线程一样共享堆,不共享栈,协程由程序

帮你搞懂Python进程,线程与协程

本文参考原文-http://bjbsair.com/2020-03-22/tech-info/4425/在操作系统中,每一个独立运行的程序,都占有 操作系统 分配的资源,这些程序中间互不干涉,都只负责运行自己的程序代码,这就是进程. 但是当操作系统频繁的创建销毁进程时,大量的系统资源被浪费在创建和销毁的过程中.而随着多核心 cpu 的出现,线程也逐渐代替了进程,成为了操作系统 可以独立运行的基本单位. 当进程不是多线程程序时,存在于进程当中的唯一线程,便是进程本身运行的代码块. 而当多线程出现在

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

进程:资源单位,由操作系统控制调度.正在执行的一个程序或者过程,进程之间不共享资源,进程间通讯手段:管道,队列,信号量等.多用于计算密集型场景,如金融计算 线程:是cpu的最小执行单位,由操作系统控制调度.一个进程至少有一个线程,同一个进程里面的多个线程共享该进程的内存资源(此处会涉及到资源的抢夺),因此需要用到“锁”,来防止资源争抢而导致的死锁等问题,创建线程的开销远远小于创建进程的开销.适用场景:IO密集型,如socket. 协程:实现单线程的并发,由用户自己通过程序来实现控制调度.修改共享

Python 中的进程、线程、协程、同步、异步、回调

进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七牛云存储的 Python 高级工程师许智翔带来了关于 Python 的分享<Python中的进程.线程.协程.同步.异步.回调>. 一.上下文切换技术 简述 在进一步之前,让我们先回顾一下各种上下文切换技术. 不过首先说明一点术语.当我们说"上下文"的时候,指的是程序在执行中的一个状态.通常我们会用调用栈来表示这个状

Python之进程、线程、协程篇

本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. threading 模块建立在 _thread 模块之上.thread 模块以低级.原始的方式来处理和控制线程,而 threading

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

本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用程序中工作的最小单元,它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. threading 模块建立在 _thread 模块之上.thread 模块以低级.原始的方式来处理和控制线程,而 threading

进程、线程、协程总结

进程.线程.协程三兄弟那些事 1.线程 定义:线程是操作系统能够进行运算调度的最小单位. 自我理解:线程开放,共享资源,线程是系统的调用资源顺序,进程是直接给CPU用的,属于耗CPU型. 特点决定用法(多线程) 2.进程 定义:程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程. 自我理解:进程保守,封装资源.程序本身是不能执行的,它只是一些静态代码,但是交给操作系统后就会读取到动态活动.进程是一堆指令的集合,至少有一个主线程.进程的运行需要分配资

Python之路(十八):进程,线程,协程

python基础之进程.线程.协程 引子 进程 线程(优先阅读) 协程 进程 概念:就是一个程序在一个数据集上的一次动态执行过程(本质上来讲,就是运行中的程序(代指运行过程),程序不运行就不是进程)    抽象概念 组成:  1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成  2.数据集:数据集则是程序在执行过程中所需要使用的资源  3.进程控制块:进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志. 阐释:进程

浅谈进程、线程和协程三者之间的区别和联系

一.进程.线程.协程 1,进程 经典定义:一个执行中程序的实例.系统中的每个程序都运行在某个进程的上下文中.(-摘自 CSAPP) 进程是系统资源分配的最小单位 2,线程(thread) 线程就是运行在进程上下文中的逻辑流. 线程是操作系统能够进行运算调度的最小单位. 3,协程 相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛. 根据维基百科对子例程的描述:是一个大型程序中的某部分代码,由一个或多个语句块组成.它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性.我可