【转】Python中的GIL、多进程和多线程
转自:http://lesliezhu.github.io/public/2015-04-20-python-multi-process-thread.html
目录
1 GIL(Global Interpretor Lock,全局解释器锁)
see:
如果其他条件不变,Python程序的执行速度直接与解释器的“速度”相关。不管你怎样优化自己的程序,你的程序的执行速度还是依赖于解释器执行你的程序的效率。
目前来说,多线程执行还是利用多核系统最常用的方式。尽管多线程编程大大好于“顺序”编程,不过即便是仔细的程序员也没法在代码中将并发性做到最好。
对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。
事实上,这个问题被问得如此频繁以至于Python的专家们精心制作了一个标准答案:”不要使用多线程,请使用多进程。“但这个答案比那个问题更加让人困惑。
GIL对诸如当前线程状态和为垃圾回收而用的堆分配对象这样的东西的访问提供着保护。然而,这对Python语言来说没什么特殊的,它需要使用一个GIL。这是该实现的一种典型产物。现在也有其它的Python解释器(和编译器)并不使用GIL。虽然,对于CPython来说,自其出现以来已经有很多不使用GIL的解释器。
不管某一个人对Python的GIL感觉如何,它仍然是Python语言里最困难的技术挑战。想要理解它的实现需要对操作系统设计、多线程编程、C语言、解释器设计和CPython解释器的实现有着非常彻底的理解。单是这些所需准备的就妨碍了很多开发者去更彻底的研究GIL。
2 threading
threading 模块提供比/基于 thread 模块更高层次的接口;如果此模块由于 thread 丢失而无法使用,可以使用 dummy_threading 来代替。
CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously.
举例:
import threading, zipfile class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print 'Finished background zip of: ', self.infile background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print 'The main program continues to run in foreground.' background.join() # Wait for the background task to finish
print 'Main program waited until background was done.'
2.1 创建线程
import threading
import datetime class ThreadClass(threading.Thread):
def run(self):
now = datetime.datetime.now()
print "%s says Hello World at time: %s" % (self.getName(), now) for i in range(2):
t = ThreadClass()
t.start()
2.2 使用线程队列
import Queue
import threading
import urllib2
import time
from BeautifulSoup import BeautifulSoup hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
"http://ibm.com", "http://apple.com"] queue = Queue.Queue()
out_queue = Queue.Queue() class ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, queue, out_queue):
threading.Thread.__init__(self)
self.queue = queue
self.out_queue = out_queue def run(self):
while True:
#grabs host from queue
host = self.queue.get() #grabs urls of hosts and then grabs chunk of webpage
url = urllib2.urlopen(host)
chunk = url.read() #place chunk into out queue
self.out_queue.put(chunk) #signals to queue job is done
self.queue.task_done() class DatamineThread(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, out_queue):
threading.Thread.__init__(self)
self.out_queue = out_queue def run(self):
while True:
#grabs host from queue
chunk = self.out_queue.get() #parse the chunk
soup = BeautifulSoup(chunk)
print soup.findAll(['title']) #signals to queue job is done
self.out_queue.task_done() start = time.time()
def main(): #spawn a pool of threads, and pass them queue instance
for i in range(5):
t = ThreadUrl(queue, out_queue)
t.setDaemon(True)
t.start() #populate queue with data
for host in hosts:
queue.put(host) for i in range(5):
dt = DatamineThread(out_queue)
dt.setDaemon(True)
dt.start() #wait on the queue until everything has been processed
queue.join()
out_queue.join() main()
print "Elapsed Time: %s" % (time.time() - start)
3 dummy_threading(threading的备用方案)
dummy_threading 模块提供完全复制了threading模块的接口,如果无法使用thread,则可以用这个模块替代.
使用方法:
try:
import threading as _threading
except ImportError:
import dummy_threading as _threading
4 thread
在Python3中叫 _thread,应该尽量使用 threading 模块替代。
5 dummy_thread(thead的备用方案)
dummy_thread 模块提供完全复制了thread模块的接口,如果无法使用thread,则可以用这个模块替代.
在Python3中叫 _dummy_thread, 使用方法:
try:
import thread as _thread
except ImportError:
import dummy_thread as _thread
最好使用 dummy_threading 来代替.
6 multiprocessing(基于thread接口的多进程)
see:
使用 multiprocessing 模块创建子进程而不是线程来克服GIL引起的问题.
举例:
from multiprocessing import Pool def f(x):
return x*x if __name__ == '__main__':
p = Pool(5)
print(p.map(f, [1, 2, 3]))
6.1 Process类
创建进程是使用Process类:
from multiprocessing import Process def f(name):
print 'hello', name if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
p.join()
6.2 进程间通信
Queue 方式:
from multiprocessing import Process, Queue def f(q):
q.put([42, None, 'hello']) if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print q.get() # prints "[42, None, 'hello']"
p.join()
Pipe 方式:
from multiprocessing import Process, Pipe def f(conn):
conn.send([42, None, 'hello'])
conn.close() if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print parent_conn.recv() # prints "[42, None, 'hello']"
6.3 同步
添加锁:
from multiprocessing import Process, Lock def f(l, i):
l.acquire()
print 'hello world', i
l.release() if __name__ == '__main__':
lock = Lock() for num in range(10):
Process(target=f, args=(lock, num)).start()
6.4 共享状态
应该尽量避免共享状态.
共享内存方式:
from multiprocessing import Process, Value, Array def f(n, a):
n.value = 3.1415927
for i in range(len(a)):
a[i] = -a[i] if __name__ == '__main__':
num = Value('d', 0.0)
arr = Array('i', range(10)) p = Process(target=f, args=(num, arr))
p.start()
p.join() print num.value
print arr[:]
Server进程方式:
from multiprocessing import Process, Manager def f(d, l):
d[1] = '1'
d['2'] = 2
d[0.25] = None
l.reverse() if __name__ == '__main__':
manager = Manager() d = manager.dict()
l = manager.list(range(10)) p = Process(target=f, args=(d, l))
p.start()
p.join() print d
print l
第二种方式支持更多的数据类型,如list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Queue, Value ,Array.
6.5 Pool类
通过Pool类可以建立进程池:
from multiprocessing import Pool def f(x):
return x*x if __name__ == '__main__':
pool = Pool(processes=4) # start 4 worker processes
result = pool.apply_async(f, [10]) # evaluate "f(10)" asynchronously
print result.get(timeout=1) # prints "100" unless your computer is *very* slow
print pool.map(f, range(10)) # prints "[0, 1, 4,..., 81]"
7 multiprocessing.dummy
在官方文档只有一句话:
multiprocessing.dummy replicates the API of multiprocessing but is no more than a wrapper around the threading module.
multiprocessing.dummy是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程;- 可以针对 IO 密集型任务和 CPU 密集型任务来选择不同的库.
IO 密集型任务选择multiprocessing.dummy,CPU 密集型任务选择multiprocessing.
举例:
import urllib2
from multiprocessing.dummy import Pool as ThreadPool urls = [
'http://www.python.org',
'http://www.python.org/about/',
'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
'http://www.python.org/doc/',
'http://www.python.org/download/',
'http://www.python.org/getit/',
'http://www.python.org/community/',
'https://wiki.python.org/moin/',
'http://planet.python.org/',
'https://wiki.python.org/moin/LocalUserGroups',
'http://www.python.org/psf/',
'http://docs.python.org/devguide/',
'http://www.python.org/community/awards/'
# etc..
] # Make the Pool of workers
pool = ThreadPool(4)
# Open the urls in their own threads
# and return the results
results = pool.map(urllib2.urlopen, urls)
#close the pool and wait for the work to finish
pool.close()
pool.join() results = []
for url in urls:
result = urllib2.urlopen(url)
results.append(result)
8 后记
- 如果选择多线程,则应该尽量使用
threading模块,同时注意GIL的影响- 如果多线程没有必要,则使用多进程模块
multiprocessing,此模块也通过multiprocessing.dummy支持多线程.- 分析具体任务是I/O密集型,还是CPU密集型
9 资源
- https://docs.python.org/2/library/threading.html
- https://docs.python.org/2/library/thread.html#module-thread
- http://segmentfault.com/a/1190000000414339
- http://www.oschina.net/translate/pythons-hardest-problem
- http://www.w3cschool.cc/python/python-multithreading.html
- Python threads: communication and stopping
- Python - parallelizing CPU-bound tasks with multiprocessing
- Python Multithreading Tutorial: Concurrency and Parallelism
- An introduction to parallel programming–using Python's multiprocessing module
- multiprocessing Basics
- Python多进程模块Multiprocessing介绍
- Multiprocessing vs Threading Python
- Parallelism in one line–A Better Model for Day to Day Threading Tasks
- 一行 Python 实现并行化 – 日常多线程操作的新思路
- 使用 Python 进行线程编程
【转】Python中的GIL、多进程和多线程的更多相关文章
- 线程安全及Python中的GIL
线程安全及Python中的GIL 本博客所有内容采用 Creative Commons Licenses 许可使用. 引用本内容时,请保留 朱涛, 出处 ,并且 非商业 . 点击 订阅 来订阅本博客. ...
- 聊聊Python中的GIL
对于广大写Python的人来说,GIL(Global Interpreter Lock, 全局解释器锁)肯定不陌生,但未必清楚GIL的历史和全貌是怎样的,今天我们就来梳理一下GIL. 1. 什么是GI ...
- 深入理解Python中的GIL(全局解释器锁)
深入理解Python中的GIL(全局解释器锁) Python是门古老的语言,要想了解这门语言的多线程和多进程以及协程,以及明白什么时候应该用多线程,什么时候应该使用多进程或协程,我们不得不谈到的一个东 ...
- Python中的GIL
•start 线程准备就绪,等待CPU调度 •setName 为线程设置名称 •getName 获取线程名称 •setDaemon 设置为后台线程或前台线程(默认) 如果是后台线程,主线程执行过程中, ...
- 为什么在python中推荐使用多进程而不是多线程(转载)
最近在看Python的多线程,经常我们会听到老手说:"Python下多线程是鸡肋,推荐使用多进程!",但是为什么这么说呢? 要知其然,更要知其所以然.所以有了下面的深入研究: GI ...
- python中的GIL详解
GIL是什么 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念.就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可 ...
- python并发编程之多进程、多线程、异步、协程、通信队列Queue和池Pool的实现和应用
什么是多任务? 简单地说,就是操作系统可以同时运行多个任务.实现多任务有多种方式,线程.进程.协程. 并行和并发的区别? 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任 ...
- python 中的GIL (全局解释器锁)详解
1.GIL是什么? GIL全称Global Interpreter Lock,即全局解释器锁. 作用就是,限制多线程同时执行,保证同一时间内只有一个线程在执行. GIL并不是Python的特性,它是在 ...
- Python - 并发编程,多进程,多线程
传送门 https://blog.csdn.net/jackfrued/article/details/79717727 在此基础上实践和改编某些点 1. 并发编程 实现让程序同时执行多个任务也就是常 ...
- Python中的GIL锁
在Python中,可以通过多进程.多线程和多协程来实现多任务. 在多线程的实现过程中,为了避免出现资源竞争问题,可以使用互斥锁来使线程同步(按顺序)执行. 但是,其实Python的CPython(C语 ...
随机推荐
- NVelocity-0.4.2.8580 的修改记录[发个vs2008能用的版本] -- "It appears that no class was specified as the ResourceManager..." bug 修正等
因为另有开发记录工具最新没怎么在 cnblog 写开发备忘.不过我觉得这个是个比较严重的问题,觉得有必要让更多的人知道处理方法,所以在 cnblog 也放上一篇希望广为传播. 因为现在网络上vs200 ...
- Sencha Touch+PhoneGap打造超级奶爸之喂养记(一) 源码免费提供
起源 非常高兴我的宝宝健康平安的出生了.对于初次做奶爸的我,喜悦过后,面临着各中担心,担心宝宝各项指标是否正常.最初几天都是在医院待着,从出生那一天开始,护士妹妹隔一段时间就会来问宝宝的喂奶,大小便, ...
- Log4Net简单使用
一. Log4net是什么.优点 用来记录程序日志,优点:1.提供应用程序运行时的精确环境,可供开发人员尽快找到应用程序中的Bug:2.日志信息可以输出到不同的地方(数据库,文件,邮箱等). 二. L ...
- android 开发 - 对图片进行虚化(毛玻璃效果,模糊)
概述 IPAD,IPHONE上首页背景的模糊效果是不是很好看,那么在 Android中如何实现呢.我通过一种方式实现了这样的效果. 开源库名称:anroid-image-blur 一个android ...
- 疑难杂症 - SQL语句整理
一.关联子查询-查日期最新列 前天在工作中遇到一条非常有用的SQL语句,想了好久愣是没搞出来.今天将这个问题模拟出来:先看表 需求是,对于每个人,仅显示时间最新的那一条记录. 答案如下: select ...
- 从抽象谈起(三):AOP编程和ASP.NET MVC
AOP(Aspect oriented programming)面向切面编程.说成切面不容易理解,代码哪里有切面?又不是三维物体.概念不管,我们从其思想来理解这个名词吧. AOP的主要思想是把相同.相 ...
- saiku缓存整理
使用saiku的人,肯定都有这么一个经历,查询了一次多维分析数据表,第二次之后就特别快,因为它缓存了结果,可问题是过了一天,甚至几天,来源数据早都更换了,可还是这个缓存结果.问题来了,缓存不失效! 那 ...
- Android对话框之dismiss和cancel和hide区别
在我们看来两者效果都是一样的,其实看下源码就知道cancel肯定会去调dismiss的,如果调用的cancel的话就可以监听DialogInterface.OnCancelListener. /** ...
- 使用最新的“huihui中文语音库”实现文本转语音功能
最近一个web项目中,需要进行语音播报,将动态的文字转换为语音(TTS)存为WAV文件后通过web播放给用户.选择了微软所提供的SAPI (The Microsoft Speech API),只需要几 ...
- 【转】每个人应该知道的NVelocity用法
NVelocity是一个基于.NET的模板引擎(template engine).它允许任何人仅仅简单的使用模板语言(template language)来引用由.NET代码定义的对象.从而使得界面设 ...