Python--多线程
来源:互联网 发布:模拟量用电流没数据 编辑:程序博客网 时间:2024/05/17 07:31
首先,说明一下多线程的应用场景:当python处理多个任务时,这些任务本质是异步的,需要有多个并发事务,各个事务的运行顺序可以是不确定的、随机的、不可预测的。计算密集型的任务可以顺序执行分隔成的多个子任务,也可以用多线程的方式处理。但I/O密集型的任务就不好以单线程方式处理了,如果不用多线程,只能用一个或多个计时器来处理实现。
下面说一下进程与线程:进程(有时叫重量级进程),是程序的一次执行,正如我们在centos中,ps -aux | grep something 的时候,总有一个他自身产生的进程,也就是这个grep进程,每个进程有自己的地址空间、内存、数据栈、及其他记录其运行轨迹的辅助数据,因此各个进程也不能直接共享信息,只能用进程间通信(IPC)。
线程(轻量级进程),与进程最大的区别是,所有的线程运行在同一个进程中,共享相同的运行环境,共享同一片数据空间。所以线程之间可以比进程之间更方便的共享数据以及相互通讯,并发执行完成事务。
为了方便理解记忆进程与线程的关系,我们可以做一个类比:把cpu比作一个搬家公司,而这家搬家公司只有一辆车(进程)来供使用,开始,这家搬家公司很穷,只有一个员工(单线程),那么,这个搬家公司一天,最多只能搬5家,后来,老板赚到钱了,他没买车,而是多雇了n个员工(多线程),这样,每个员工会被安排每次只搬一家,然后就去休息,把车让出来,让其他人搬下一家,这看起来其实并没有提高多少效率,反而增加了成本是吧,这是因为GIL(Global Interpreter Lock) 全局解释器锁,保证了线程安全(保证数据被安全读取),即同时只能有一个线程在CPU上运行,这是python特有的机制,也就是说,即使你的运行环境具有双CPU,python虚拟机也只会使用一个cpu,也就是说GIL 直接导致 CPython 不能利用物理多核的性能加速运算。具体的详细解释(历史遗留问题,硬件发展太快)可以参考这篇博客:
http://blog.sina.com.cn/s/blog_64ecfc2f0102uzzf.html
在python核心编程中,作者极力建议我们不要使用thread模块,而是要使用threading模块,原因如下:
1、当主线程退出时,所有其他线程没有被清除就退出了,thread模块无法保护所有子线程的安全退出。即,thread 模块不支持守护进程。
2、thread模块的属性有可能会与threading出现冲突。
3、低级别的thread模块的同步原语很少(实际只有一个,应该是sleep)。
一、thread模块
以下是不使用GIL和使用GIL的两个示例代码:
1.不使用GIL的代码示例:
from time import sleep,ctimeimport threaddef loop0(): print 'start loop 0 at: ',ctime() sleep(4) print 'loop 0 done at: ',ctime()def loop1(): print 'start loop 1 at: ',ctime() sleep(2) print 'loop 1 done at: ',ctime()def main(): print 'start at: ',ctime() thread.start_new_thread(loop0,()) thread.start_new_thread(loop1,()) sleep(6) print 'all loop is done, ' ,ctime()if __name__=='__main__': main()
输出结果:
start at: Thu Jan 28 10:46:27 2016
start loop 0 at: Thu Jan 28 10:46:27 2016
start loop 1 at: Thu Jan 28 10:46:27 2016
loop 1 done at: Thu Jan 28 10:46:29 2016
loop 0 done at: Thu Jan 28 10:46:31 2016
all loop is done, Thu Jan 28 10:46:33 2016
由以上输出可以看出,我们成功开启了两个线程,并且与主线程同步,在第2s时,loop1先完成,第4s时loop0完成,又过了2s,主线程完成结束。整个主线程经过了6s,loop0和loop1同步完成。
2、使用GIL的代码示例:
import threadfrom time import sleep,ctimeloops = [4,2]def loop(nloop,nsec,lock): print 'start loop',nloop,'at: ',ctime() sleep(nsec) print 'loop',nloop,'done at:',ctime() lock.release()def main(): print 'starting at:',ctime() locks = [] nloops = range(len(loops)) for i in nloops: lock = thread.allocate_lock() #创建锁的列表,存在locks中 lock.acquire() locks.append(lock) for i in nloops: thread.start_new_thread(loop,(i,loops[i],locks[i])) #创建线程,参数为循环号,睡眠时间,锁 for i in nloops: while locks[i].locked(): #等待循环完成,解锁 pass print 'all DONE at:',ctime()if __name__ == '__main__': main()
以上输出如下:
starting at: Thu Jan 28 14:59:22 2016
start loop 0 at: Thu Jan 28 14:59:22 2016
start loop 1 at: Thu Jan 28 14:59:22 2016
loop 1 done at: Thu Jan 28 14:59:24 2016
loop 0 done at: Thu Jan 28 14:59:26 2016
all DONE at: Thu Jan 28 14:59:26 2016
历时4秒,这样效率得到提高,也比在主线程中用一个sleep()函数来计时更为合理。
二、threading模块
1、Thread类
在thread类中,可以用以下三种方法来创建线程:
(1)创建一个thread实例,传给它一个函数
(2)创建一个thread实例,传给它一个可调用的类对象
(3)从thread派生出一个子类,创建这个子类的对象
方法(1)
__author__ = 'dell'import threadingfrom time import sleep,ctimedef loop0(): print 'start loop 0 at:',ctime() sleep(4) print 'loop 0 done at:',ctime()def loop1(): print 'start loop 1 at:',ctime() sleep(2) print 'loop 1 done at:',ctime()def main(): print 'starting at:',ctime() threads = [] t1 = threading.Thread(target=loop0,args=()) #创建线程 threads.append(t1) t2 = threading.Thread(target=loop1,args=()) threads.append(t2) for t in threads: t.setDaemon(True)<span style="white-space:pre"></span> #开启守护线程(一定要在start()前调用) t.start()<span style="white-space:pre"></span> #开始线程执行 for t in threads:<span style="white-space:pre"></span> t.join()<span style="white-space:pre"></span> #将程序挂起阻塞,直到线程结束,如果给出数值,则最多阻塞timeout秒if __name__ == '__main__': main() print 'All DONE at:',ctime()
在这里,就不用像thread模块那样要管理那么多锁(分配、获取、释放、检查等)了,同时我也减少了循环的代码,直接自己编号循环了,得到输出如下:
starting at: Thu Jan 28 16:38:14 2016
start loop 0 at: Thu Jan 28 16:38:14 2016
start loop 1 at: Thu Jan 28 16:38:14 2016
loop 1 done at: Thu Jan 28 16:38:16 2016
loop 0 done at: Thu Jan 28 16:38:18 2016
All DONE at: Thu Jan 28 16:38:18 2016
结果相同,但是从代码的逻辑来看,要清晰的多了。其他两种在此就不贴出代码了。实例化一个Thread与调用thread.start_new_thread直接最大的区别就是新的线程不会立即开始执行,也就是说,在threading模块的Thread类中当我们实例化之后,再调用.start()函数后被统一执行,这使得我们的程序具有很好的同步特性。
下面是单线程与多线程的一个对比示例,分别以乘除完成两组运算,从而看出多线程对效率的提高
from time import ctime,sleepimport threadingdef multi(): num1 = 1 print 'start mutiple at:',ctime() for i in range(1,10): num1 = i*num1 sleep(0.2) print 'mutiple finished at:',ctime() return num1def divide(): num2 = 100 print 'start division at:',ctime() for i in range(1,10): num2 = num2/i sleep(0.4) print 'division finished at:',ctime() return num2def main(): print '---->single Thread' x1 = multi() x2 = divide() print 'The sum is ',sum([x1,x2]),'\nfinished singe thread',ctime() print '----->Multi Thread' threads = [] t1 = threading.Thread(target=multi,args=()) threads.append(t1) t2 = threading.Thread(target=divide,args=()) threads.append(t2) for t in threads: t.setDaemon(True) t.start() for t in threads: t.join()if __name__ == '__main__': main()
结果如下:
---->single Thread
start mutiple at: Thu Jan 28 21:41:18 2016
mutiple finished at: Thu Jan 28 21:41:20 2016
start division at: Thu Jan 28 21:41:20 2016
division finished at: Thu Jan 28 21:41:24 2016
The sum is 362880
finished singe thread Thu Jan 28 21:41:24 2016
----->Multi Thread
start mutiple at: Thu Jan 28 21:41:24 2016
start division at: Thu Jan 28 21:41:24 2016
mutiple finished at: Thu Jan 28 21:41:26 2016
division finished at: Thu Jan 28 21:41:27 2016
The sum is : 362880
- Python多线程
- Python多线程
- Python多线程
- python多线程
- python 多线程
- python多线程
- python多线程
- python 多线程
- python多线程
- Python多线程
- Python 多线程
- python多线程
- python 多线程
- {python多线程}
- Python 多线程
- Python多线程
- python 多线程
- Python 多线程
- poj1426 Find The Multiple 题解
- 前端图形化展示js
- windows下如何github ssh 公钥
- 快速掌握Lua 5.3 —— 数据操作
- Greenplum数据库整体架构
- Python--多线程
- CS模式下的自动填充框(高效率,解决输入法多次回发问题并进行延迟处理)
- ubuntu server安装桌面
- c#之加加减减的优先级
- 在 QT 中使用 libusb 检测 MAC 上的 USB 设备
- centos7 开机 自动连网
- 超全!整理常用的iOS第三方资源
- 跟我一起从零开始学WCF系列课程
- 关于百度软件,百度软件真是垃圾的没法没法的