Python并发机制(二)——多线程

来源:互联网 发布:如何用网络电视连手机 编辑:程序博客网 时间:2024/05/21 09:32

Python并发机制(二)——多线程

实例化threading.Thread()

import threading,timedef fn(n):  print n  print str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))def main():  tpool = []  for i in range(10):    t = threading.Thread(target=fn,args=(0,))    tpool.append(t)  for t in tpool:    t.start()  for t in tpool:    threading.Thread.join(t)if __main__ == '__name__':  main()

运行结果如下:

1 12016-11-22 01:22:292016-11-22 01:22:29112016-11-22 01:22:292016-11-22 01:22:2912016-11-22 01:22:29 12016-11-22 01:22:2912016-11-22 01:22:291 12016-11-22 01:22:29 12016-11-22 01:22:292016-11-22 01:22:29

自定义线程类的实例化

import threading,timeclass myThread(threading.Thread):  def __init__(self,n):    threading.Thread.__init__(self)    self.n = n  def fn(self):    print self.n    print str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))  def run(self):    self.fn()def main():  tpool = []  for i in range(10):    tpool.append(myThread(1))  for t in tpool:    t.start()  for t in tpool:    t.join()if __name__ == '__main__':  main()

运行结果如下:

12016-11-22 01:46:361 12016-11-22 01:46:362016-11-22 01:46:36 12016-11-22 01:46:361 12016-11-22 01:46:362016-11-22 01:46:3612016-11-22 01:46:3612016-11-22 01:46:3612016-11-22 01:46:36 12016-11-22 01:46:36

使用multiprocesssing.dummy执行多线程任务

#coding=utf-8import urllib2import timefrom multiprocesssing.dummy import Poolimport threadingfrom multiprocesssing import Pool as PPoolurls = [ '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', '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/',]#单线程时间start = time.time()result = map(urllib2.urlopen,urls)t1 = time.time() - startprint "单线程时间:",t1#4线程时间time.sleep(1)start = time.time()pool = Pool(4)result = pool.map(urllib2.urlopen,urls)pool.close()pool.join()t4 = time.time() - startprint "4线程时间:",t4#8线程时间time.sleep(1)start = time.time()pool = Pool(12)result = pool.map(urllib2.urlopen,urls)pool.close()pool.join()t8 = time.time() - startprint "12线程时间:",t8#threading模块4线程time.sleep(1)start = time.time()t = []for i in range(4):    t.append(threading.Thread(target=map,args=(urllib2.urlopen,urls[i*3:i*3+3])))for th in t:    th.start()for th in t:    th.join()t = time.time() - startprint "threading模块4线程:",t#4进程time.sleep(1)start = time.time()pool = PPool(processes=4)#注意加_async,非阻塞版本results = pool.map_async(urllib2.urlopen,urls)pool.close()pool.join()t = time.time() - startprint "4进程:",t

运行结果如下:

单线程时间: 23.56174492844线程时间: 5.8167810440112线程时间: 2.70931982994threading模块4线程: 4.121351957324进程: 5.78697896004

注意:受GIL机制(如下)影响的多线程实质上是交替单线程执行,利用multiproceessing. dummy.Pool实例化的线程池能实现并发,类似于进程池,如果将它们分为两类,则第一类更有利于IO密集型程序(如上例偏IO),第二类更有利于CPU密集型程序

python(CPython)全局锁(GIL)机制

首先需要明确的一点是 GIL 并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把 GIL 归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

多个线程如果共享一个变量,它们都有可能对变量进行修改,但高级语言的一条语句在CPU执行时是若干步,即使一个简单的计算common = common + 1也分为至少两步:
1. 求和common和1并将值转存到一个临时变量
2. 将临时变量值赋给common

由于线程是交替执行的,所以循环次数足够多时可能出现一个线程过程中执行另一个线程的第一步或第二步,如下:

import time,threadingcommon = 0def change(n):  global common  for i in range(100000):    common = common + n    common = common - nt1 = threading.Thread(target=change,args=(5,))t2 = threading.Thread(target=change,args=(8,))t1.start()t2.start()t1.join()t2.join()print common

运行结果如下:

261

所以我们运用线程锁(thrading.Lock())

import time,threadingcommon = 0lock = threading.Lock()def change(n):  global common  for i in range(100000):    lock.acquire()    try:      common = common + n      common = common - n    #一定会释放锁    finally:      lock.release()t1 = threading.Thread(target=change,args=(5,))t2 = threading.Thread(target=change,args=(8,))t1.start()t2.start()t1.join()t2.join()print common

运行结果为0

GIL解决了线程间数据一致性和状态同步的困难,但无疑大多数情况下它是低效的,但当大家开始抱怨并试图去拆分和去除GIL的时候,却发现大量库代码开发者已经重度依赖GIL而非常难以去除了。所以GIL的现存更多的是历史原因.

0 0