- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议48:使用 threading 模块编写多线程程序
GIL的存在使得Python多线程编程暂时无法充分利用多处理器的优势,这种限制也许使很多人感到沮丧,但事实上这并不意味着我们需要放弃多线程。的确,对于只含纯Python的代码也许使用多线程并不能提高运行速率,但在以下几种情况,如等待外部资源返回,或者为了提高用户体验而建立反应灵活的用户界面,或者多用户应用程序中,多线程仍然是一个比较好的解决方案。Python为多线程编程提供了两个非常简单明了的模块:thread和threading。那么,这两个模块在多线程处理上有什么区别呢?简单一点说:thread模块提供了多线程底层支持模块,以低级原始的方式来处理和控制线程,使用起来较为复杂;而threading模块基于thread进行包装,将线程的操作对象化,在语言层面提供了丰富的特性。Python多线程支持用两种方式来创建线程:一种是通过继承Thread类,重写它的run()方法(注意,不是start()方法);另一种是创建一个threading.Thread对象,在它的初始化函数(__init__())中将可调用对象作为参数传入。实际应用中,推荐优先使用threading模块而不是thread模块,(除非有特殊需要)。下面来具体分析一下这么做的原因。
1)threading模块对同步原语的支持更为完善和丰富。就线程的同步和互斥来说,thread模块只提供了一种锁类型thread.LockType,而threading模块中不仅有Lock指令锁、RLock可重入指令锁,还支持条件变量Condition、信号量Semaphore、BoundedSemaphore以及Event事件等。
2)threading模块在主线程和子线程交互上更为友好,threading中的join()方法能够阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。利用该方法可以方便地控制主线程和子线程以及子线程之间的执行。来看一个简单示例:
import threading, time,sys class test(threading.Thread): def __init__(self,name,delay): threading.Thread.__init__(self) self.name = name self.delay = delay def run(self): print "%s delay for %s" %(self.name,self.delay) time.sleep(self.delay) c = 0 while True: print "This is thread %s on line %s" %(self.name,c) c = c + 1 if c == 3: print "End of thread %s" % self.name break t1 = test('Thread 1', 2) t2 = test('Thread 2', 2) t1.start() print "Wait t1 to end" t1.join() t2.start() print 'End of main'
上面的例子中,主线程main在t1上使用join()的方法,主线程会等待t1结束后才继续运行后面的语句,由于线程t2的启动在join语句之后,t2一直等到t1退出后才会开始运行。输出结果如图4-6所示。
图4-6 多线程示例输出结果
3)thread模块不支持守护线程。thread模块中主线程退出的时候,所有的子线程不论是否还在工作,都会被强制结束,并且没有任何警告也没有任何退出前的清理工作。来看一个例子:
from thread import start_new_thread import time def myfunc(a,delay): print "I will calculate square of %s after delay for %s" %(a,delay) time.sleep(delay) print "calculate begins..." result = a*a print result return result start_new_thread(myfunc,(2,5))# 同时启动两个线程 start_new_thread(myfunc,(6,8)) time.sleep(1)
运行程序,输出结果如下,你会发现子线程的结果还未返回就已经结束了。
I will calculate square of 2 after delay for 5I will calculate square of 6 after delya for 2
这是一种非常野蛮的主线程和子线程的交互方式。如果把主线程和子线程组成的线程组比作一个团队的话,那么主线程应该是这个团队的管理者,它了解每个线程所做的事情、所需的数据输入以及子线程结束时的输出,并把各个线程的输出组合形成有意义的结果。如果一个团队中管理者采取这种强硬的管理方式,相信很多下属都会苦不堪言,因为不仅没有被尊重的感觉,而且还有可能因为这种强势带来决策上的失误。实际上很多情况下我们可能希望主线程能够等待所有子线程都完成时才退出,这时应该使用threading模块,它支持守护线程,可以通过setDaemon()函数来设定线程的daemon属性。当daemon属性设置为True的时候表明主线程的退出可以不用等待子线程完成。默认情况下,daemon标志为False,所有的非守护线程结束后主线程才会结束。来看具体的例子,当daemon属性设置为False,默认主线程会等待所有子线程结束才会退出。将t2的daemon属性改为True之后即使t2运行未结束主线程也会直接退出。
import threading import time def myfunc(a,delay): print "I will calculate square of %s after delay for %s" %(a,delay) time.sleep(delay) print "calculate begins..." result = a*a print result return result t1=threading.Thread(target=myfunc,args=(2,5)) t2=threading.Thread(target=myfunc,args=(6,8)) print t1.isDaemon() print t2.isDaemon() t2.setDaemon(True) # 设置守护线程 t1.start() t2.start()
4)Python3中已经不存在thread模块。thread模块在Python3中被命名为_thread,这种更改主要是为了更进一步明确表示与thread模块相关的更多的是具体的实现细节,它更多展示的是操作系统层面的原始操作和处理。在一般的代码中不应该选择thread模块。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论