您的位置:首页 > 编程语言 > Python开发

python基础===进程,线程,协程的区别(转)

2018-01-18 08:51 543 查看

本文转自:http://blog.csdn.net/hairetz/article/details/16119911  

  • 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
  • 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
  • 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

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

打个比方吧,假设有一个操作系统,是单核的,系统上没有其他的程序需要运行,有两个线程 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 秒。

#!/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 …

#!/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

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

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

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

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: