Python多线程原理与用法实例剖析


Posted in Python onJanuary 22, 2019

本文实例讲述了Python多线程原理与用法。分享给大家供大家参考,具体如下:

先来看个栗子:

下面来看一下I/O秘籍型的线程,举个栗子——爬虫,下面是爬下来的图片用4个线程去写文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
import urllib
import threading
import Queue
import timeit
def getHtml(url):
  html_page = urllib.urlopen(url).read()
  return html_page
# 提取网页中图片的URL
def getUrl(html):
  pattern = r'src="(http://img.*?)"' # 正则表达式
  imgre = re.compile(pattern)
  imglist = re.findall(imgre, html) # re.findall(pattern,string) 在string中寻找所有匹配成功的字符串,以列表形式返回值
  return imglist
class getImg(threading.Thread):
  def __init__(self, queue, thread_name=0): # 线程公用一个队列
    threading.Thread.__init__(self)
    self.queue = queue
    self.thread_name = thread_name
    self.start() # 启动线程
  # 使用队列实现进程间通信
  def run(self):
    global count
    while (True):
      imgurl = self.queue.get() # 调用队列对象的get()方法从队头删除并返回一个项目
      urllib.urlretrieve(imgurl, 'E:\mnt\girls\%s.jpg' % count)
      count += 1
      if self.queue.empty():
        break
      self.queue.task_done() # 当使用者线程调用 task_done() 以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。
imglist = []
def main():
  global imglist
  url = "http://huaban.com/favorite/beauty/" # 要爬的网页地址
  html = getHtml(url)
  imglist = getUrl(html)
def main_1():
  global count
  threads = []
  count = 0
  queue = Queue.Queue()
  # 将所有任务加入队列
  for img in imglist:
    queue.put(img)
  # 多线程爬去图片
  for i in range(4):
    thread = getImg(queue, i)
    threads.append(thread)
  # 阻塞线程,直到线程执行完成
  for thread in threads:
    thread.join()
if __name__ == '__main__':
  main()
  t = timeit.Timer(main_1)
  print t.timeit(1)

4个线程的执行耗时为:0.421320716723秒

修改一下main_1换成单线程的:

def main_1():
  global count
  threads = []
  count = 0
  queue = Queue.Queue()
  # 将所有任务加入队列
  for img in imglist:
    queue.put(img)
  # 多线程爬去图片
  for i in range(1):
    thread = getImg(queue, i)
    threads.append(thread)
  # 阻塞线程,直到线程执行完成
  for thread in threads:
    thread.join()

单线程的执行耗时为:1.35626623274秒

Python多线程原理与用法实例剖析

再来看一个:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import timeit
def countdown(n):
  while n > 0:
    n -= 1
def task1():
  COUNT = 100000000
  thread1 = threading.Thread(target=countdown, args=(COUNT,))
  thread1.start()
  thread1.join()
def task2():
  COUNT = 100000000
  thread1 = threading.Thread(target=countdown, args=(COUNT // 2,))
  thread2 = threading.Thread(target=countdown, args=(COUNT // 2,))
  thread1.start()
  thread2.start()
  thread1.join()
  thread2.join()
if __name__ == '__main__':
  t1 = timeit.Timer(task1)
  print "countdown in one thread ", t1.timeit(1)
  t2 = timeit.Timer(task2)
  print "countdown in two thread ", t2.timeit(1)

task1是单线程,task2是双线程,在我的4核的机器上的执行结果:

countdown in one thread  3.59939150155

countdown in two thread  9.87704289712

天呐,双线程比单线程计算慢了2倍多,这是为什么呢,因为countdown是CPU密集型任务(计算嘛)

Python多线程原理与用法实例剖析

I/O密集型任务:线程做I/O处理的时候会释放GIL,其他线程获得GIL,当该线程再做I/O操作时,又会释放GIL,如此往复;

CPU密集型任务:在多核多线程比单核多线程更差,原因是单核多线程,每次释放GIL,唤醒的哪个线程都能获取到GIL锁,所以能够无缝执行(单核多线程的本质就是顺序执行),但多核,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0(CPU0上可能不止一个线程)拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python获取文件后缀名及批量更新目录下文件后缀名的方法
Nov 11 Python
使用Python脚本在Linux下实现部分Bash Shell的教程
Apr 17 Python
深入理解Python中range和xrange的区别
Nov 26 Python
Python+matplotlib实现华丽的文本框演示代码
Jan 22 Python
Python实现线程状态监测简单示例
Mar 28 Python
python去除扩展名的实例讲解
Apr 23 Python
Flask模拟实现CSRF攻击的方法
Jul 24 Python
使用python将图片按标签分入不同文件夹的方法
Dec 08 Python
对python中的os.getpid()和os.fork()函数详解
Aug 08 Python
TensorFlow索引与切片的实现方法
Nov 20 Python
python使用rsa非对称加密过程解析
Dec 28 Python
python对批量WAV音频进行等长分割的方法实现
Sep 25 Python
python解析含有重复key的json方法
Jan 22 #Python
Python设计模式之简单工厂模式实例详解
Jan 22 #Python
对python字典过滤条件的实例详解
Jan 22 #Python
python实现淘宝秒杀脚本
Jun 23 #Python
python实现网页自动签到功能
Jan 21 #Python
python实现桌面壁纸切换功能
Jan 21 #Python
在Python中通过getattr获取对象引用的方法
Jan 21 #Python
You might like
PHP 编写的 25个游戏脚本
2009/05/11 PHP
php学习笔记之 函数声明(二)
2011/06/09 PHP
如何使用PHP计算上一个月的今天
2013/05/23 PHP
php类声明和php类使用方法示例分享
2014/03/29 PHP
两个php日期控制类实例
2014/12/09 PHP
PHP如何使用JWT做Api接口身份认证的实现
2020/02/03 PHP
goto语法在PHP中的使用教程
2020/09/17 PHP
javascript 有用的脚本函数
2009/05/07 Javascript
juqery 学习之三 选择器 简单 内容
2010/11/25 Javascript
javascript笔记 String类replace函数的一些事
2011/09/22 Javascript
用js小类库获取浏览器的高度和宽度信息
2012/01/15 Javascript
js异步加载的三种解决方案
2013/03/04 Javascript
关于textarea提交的内容无法换行的解决办法
2013/04/09 Javascript
jQuery获取当前对象标签名称的方法
2014/02/07 Javascript
JavaScript代码复用模式详解
2014/11/07 Javascript
javascript面向对象之共享成员属性与方法及prototype关键字用法
2015/01/13 Javascript
javascript实现根据iphone屏幕方向调用不同样式表的方法
2015/07/13 Javascript
解决angular的post请求后SpringMVC后台接收不到参数值问题的方法
2015/12/10 Javascript
javascript时间戳和日期字符串相互转换代码(超简单)
2016/06/22 Javascript
jQuery 插件实现随机自由弹跳气泡样式
2017/01/12 Javascript
vue2.0实现倒计时的插件(时间戳 刷新 跳转 都不影响)
2017/03/30 Javascript
js CSS3实现卡牌旋转切换效果
2017/07/04 Javascript
原生JS实现的简单小钟表功能示例
2018/08/30 Javascript
微信小程序使用map组件实现解析经纬度功能示例
2019/01/22 Javascript
小程序实现列表多个批量倒计时
2021/01/29 Javascript
三步搞定:Vue.js调用Android原生操作
2020/09/07 Javascript
python去掉字符串中重复字符的方法
2014/02/27 Python
Python可跨平台实现获取按键的方法
2015/03/05 Python
深入浅析ImageMagick命令执行漏洞
2016/10/11 Python
用python脚本24小时刷浏览器的访问量方法
2018/12/07 Python
python重试装饰器的简单实现方法
2019/01/31 Python
使用python快速在局域网内搭建http传输文件服务的方法
2019/11/14 Python
2014财务人员自我评价范文
2014/09/21 职场文书
群众路线四风对照检查材料
2014/11/04 职场文书
2015年学校办公室工作总结
2015/05/26 职场文书
Mysql 用户权限管理实现
2021/05/25 MySQL