Python用threading实现多线程详解


Posted in Python onFebruary 03, 2017

多线程

多线程是个提高程序运行效率的好办法,本来要顺序执行的程序现在可以并行执行,可想而知效率要提高很多。但是多线程也不是能提高所有程序的效率。程序的两个极端是‘CPU 密集型'和‘I/O 密集型'两种,多线程技术比较适用于后者,因为在串行结构中当你去读写磁盘或者网络通信的时候 CPU 是闲着的,毕竟网络比磁盘要慢几个数量级,磁盘比内存慢几个数量级,内存又比 CPU 慢几个数量级。多线程技术就可以同时执行,比如你的程序需要发送 N 个 http 数据包(10 秒),还需要将文件从一个位置复制到另一个位置(20 秒),然后还需要统计另一个文件中'hello,world'字符串的出现次数(4 秒),现在一共是要用 34 秒。但是因为这些操作之间没有关联,所以可以写成多线程程序,几乎只需要 20 秒就完成了。这是针对 I/O 密集型的,如果是 CPU 密集型的就不行了。比如我的程序要计算 1000 的阶乘(10 秒),还要计算 100000 的累加(5 秒),那么即使程序是并行的,还是会要用 15 秒,甚至更多。因为当程序使用 CPU 的时候 CPU 是通过轮转来执行的,IO 密集型的程序可以在 IO 的同时用 CPU 计算,但是这里的 CPU 密集型就只能先执行一会儿线程 1 再执行一会儿线程 2。所以就需要 15 秒,甚至会更多,因为 CPU 在切换的时候需要耗时。解决 CPU 密集型程序的多线程问题就是 CPU 的事情了,比如 Intel 的超线程技术,可以在同一个核心上真正的并行两个线程,所以称之为‘双核四线程'或者‘四核八线程',我们这里具体的先不谈,谈我也不知道。

Python 骗人

说了这么多多线程的好处,但是其实 Python 不支持真正意义上的多线程编程。在 Python 中有一个叫做 GIL 的东西,中文是 全局解释器 ,这东西控制了 Python,让 Python 只能同时运行一个线程。相当于说真正意义上的多线程是由 CPU 来控制的,Python 中的多线程由 GIL 控制。如果有一个 CPU 密集型程序,用 C 语言写的,运行在一个四核处理器上,采用多线程技术的话最多可以获得 4 倍的效率提升,但是如果用 Python 写的话并不会有提高,甚至会变慢,因为线程切换的问题。所以 Python 多线程相对更加适合写 I/O 密集型程序,再说了真正的对效率要求很高的 CPU 密集型程序都用 C/C++ 去了。

第一个多线程

Python 中多线程的库一般用thread和threading这两个,thread不推荐新手和一般人使用,threading模块就相当够用了。

有一个程序,如下。两个循环,分别休眠 3 秒和 5 秒,串行执行的话需要 8 秒。

#!/usr/bin/env python
# coding=utf-8
import time
def sleep_3():
 time.sleep(3)
def sleep_5():
 time.sleep(5)
if __name__ == '__main__':
 start_time = time.time()
 print 'start sleep 3'
 sleep_3()
 print 'start sleep 5'
 sleep_5()
 end_time = time.time()
 print str(end_time - start_time) + ' s'

输出是这样的

start sleep 3
start sleep 5
8.00100016594 s

然后我们对它进行修改,使其变成多线程程序,虽然改动没有几行。首先引入了 threading 的库,然后实例化一个 threading.Thread 对象,将一个函数传进构造方法就行了。然后调用 Thread 的 start 方法开始一个线程。join() 方法可以等待该线程结束,就像我下面用的,如果我不加那两个等待线程结束的代码,那么就会直接执行输出时间的语句,这样一来统计的时间就不对了。

#!/usr/bin/env python
# coding=utf-8
import time
import threading # 引入threading
def sleep_3():
 time.sleep(3)
def sleep_5():
 time.sleep(5)
if __name__ == '__main__':
 start_time = time.time()
 print 'start sleep 3'
 thread_1 = threading.Thread(target=sleep_3)  # 实例化一个线程对象,使线程执行这个函数
 thread_1.start()  # 启动这个线程
 print 'start sleep 5'
 thread_2 = threading.Thread(target=sleep_5)  # 实例化一个线程对象,使线程执行这个函数
 thread_2.start()  # 启动这个线程
 thread_1.join()  # 等待thread_1结束
 thread_2.join()  # 等待thread_2结束
 end_time = time.time()
 print str(end_time - start_time) + ' s'

执行结果是这样的

start sleep 3
start sleep 5
5.00099992752 s

daemon 守护线程

在我们理解中守护线程应该是很重要的,类比于 Linux 中的守护进程。但是在threading.Thread中偏偏不是。

如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出的时候不需要等待这个线程执行完成。 ---------《Python 核心编程 第三版》

在 Thread 对象中默认所有线程都是非守护线程,这里有两个例子说明区别。这段代码执行的时候就没指定my_thread的daemon属性,所以默认为非守护,所以进程等待他结束。最后就可以看到 100 个 hello,world

#!/usr/bin/env python
# coding=utf-8
import threading
def hello_world():
 for i in range(100):
  print 'hello,world'
if __name__ == '__main__':
 my_thread = threading.Thread(target=hello_world)
 my_thread.start()

这里设置了my_thread为守护线程,所以进程直接就退出了,并没有等待他的结束,所以我们看不到 100 个 hello,world 只有几个而已。甚至还会抛出一个异常告诉我们有线程没结束。

#!/usr/bin/env python
# coding=utf-8
import threading
def hello_world():
 for i in range(100):
  print 'hello,world'
if __name__ == '__main__':
 my_thread = threading.Thread(target=hello_world)
 my_thread.daemon = True # 设置了标志位True
 my_thread.start()

传个参数

之前的代码都是直接执行一段代码,没有过参数的传递,那么怎么传递参数呢?其实还是很简单的。threading.Thread(target=hello_world, args=('hello,', 'world'))就可以了。args 后面跟的是一个元组,如果没有参数可以不写,如果有参数就直接在元组里按顺序添加就行了。

#!/usr/bin/env python
# coding=utf-8
import threading
def hello_world(str_1, str_2):
 for i in range(10):
  print str_1 + str_2
if __name__ == '__main__':
 my_thread = threading.Thread(target=hello_world, args=('hello,', 'world')) # 这里传递参数
 my_thread.start()

再来个多线程

threading 有三种创建 Thread 对象的方式,但是一般只会用到两种,一种是上面0X02说的传个函数进去,另一种就是这里说的继承threading.Thread。在这儿我们自己定义了两个类,类里重写了 run() 方法,也就是调用 start() 之后执行的代码,开启线程就和之前开启是一样的。之前的方式更面向过程,这个更面向对象。

#!/usr/bin/env python
# coding=utf-8
import threading
class MyThreadHello(threading.Thread):
 def run(self):
  for i in range(100):
   print 'hello'
class MyThreadWorld(threading.Thread):
 def run(self):
  for i in range(100):
   print 'world'
if __name__ == '__main__':
 thread_hello = MyThreadHello()
 thread_world = MyThreadWorld()
 thread_hello.start()
 thread_world.start()

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
压缩包密码破解示例分享(类似典破解)
Jan 17 Python
Python对两个有序列表进行合并和排序的例子
Jun 13 Python
Django框架中数据的连锁查询和限制返回数据的方法
Jul 17 Python
Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例
Dec 12 Python
PyCharm 常用快捷键和设置方法
Dec 20 Python
Python面向对象总结及类与正则表达式详解
Apr 18 Python
Django1.11自带分页器paginator的使用方法
Oct 31 Python
Python如何使用BeautifulSoup爬取网页信息
Nov 26 Python
python实现每天自动签到领积分的示例代码
Aug 18 Python
容易被忽略的Python内置类型
Sep 03 Python
Python+OpenCV检测灯光亮点的实现方法
Nov 02 Python
python常见的占位符总结及用法
Jul 02 Python
win10环境下python3.5安装步骤图文教程
Feb 03 #Python
python strip() 函数和 split() 函数的详解及实例
Feb 03 #Python
利用python画一颗心的方法示例
Jan 31 #Python
利用Python脚本生成sitemap.xml的实现方法
Jan 31 #Python
利用python实现命令行有道词典的方法示例
Jan 31 #Python
Python爬虫包 BeautifulSoup  递归抓取实例详解
Jan 28 #Python
python 编程之twisted详解及简单实例
Jan 28 #Python
You might like
解析如何屏蔽php中的phpinfo()函数
2013/06/06 PHP
Laravel实现自定义错误输出内容的方法
2016/10/10 PHP
ThinkPHP中类的构造函数_construct()与_initialize()的区别详解
2017/03/13 PHP
Web层改进II-用xmlhttp 无声息提交复杂表单
2007/01/22 Javascript
JavaScript的递归之递归与循环示例介绍
2013/08/05 Javascript
jQuery Validate 验证,校验规则写在控件中的具体实例
2014/02/27 Javascript
jQuery scroll事件实现监控滚动条分页示例
2014/04/04 Javascript
JavaScript两种跨域技术全面介绍
2014/04/16 Javascript
JavaScript设计模式之观察者模式(发布者-订阅者模式)
2014/09/24 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
2015/03/31 Javascript
浅析$.getJSON异步请求和同步请求
2016/06/06 Javascript
JS中利用localStorage防止页面动态添加数据刷新后数据丢失
2017/03/10 Javascript
微信小程序 获取二维码实例详解
2017/06/23 Javascript
使用Node.js实现RESTful API的示例
2017/08/01 Javascript
Vue-cli@3.0 插件系统简析
2018/09/05 Javascript
atom-design(Vue.js移动端组件库)手势组件使用教程
2019/05/16 Javascript
详解NodeJs项目 CentOs linux服务器线上部署
2019/09/16 NodeJs
关于vue里页面的缓存详解
2019/11/04 Javascript
微信小程序在text文本实现多种字体样式
2019/11/08 Javascript
原生JS实现贪吃蛇小游戏
2020/03/09 Javascript
Python将一个CSV文件里的数据追加到另一个CSV文件的方法
2018/07/04 Python
10 行 Python 代码教你自动发送短信(不想回复工作邮件妙招)
2018/10/11 Python
基于python实现ROC曲线绘制广场解析
2020/06/28 Python
python PyAUtoGUI库实现自动化控制鼠标键盘
2020/09/09 Python
GIVENCHY纪梵希官方旗舰店:高定彩妆与贵族护肤品
2018/04/16 全球购物
学校元旦晚会方案
2014/02/19 职场文书
班级学习雷锋活动总结
2014/07/04 职场文书
2014县政府领导班子对照检查材料思想汇报
2014/09/25 职场文书
2014年科室工作总结范文
2014/12/19 职场文书
收银员岗位职责
2015/02/03 职场文书
2016年“抗战胜利纪念日”71周年校园广播稿
2015/12/18 职场文书
2016国庆节67周年红领巾广播稿
2015/12/18 职场文书
自考生自我评价
2019/06/21 职场文书
教你使用TensorFlow2识别验证码
2021/06/11 Python
详解Flutter和Dart取消Future的三种方法
2022/04/07 Java/Android
一文搞懂PHP中的抽象类和接口
2022/05/25 PHP