先来看个栗子:

下面来看一下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秒

再来看一个:

 #!/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密集型任务(计算嘛)

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

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

作者:Andy
出处:http://www.cnblogs.com/onepiece-andy/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

python 多线程剖析的更多相关文章

  1. Day9 - Python 多线程、进程

    Python之路,Day9, 进程.线程.协程篇   本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线 ...

  2. Python 多线程、进程

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  3. Python多线程、进程、协程

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  4. python多线程学习记录

    1.多线程的创建 import threading t = t.theading.Thread(target, args--) t.SetDeamon(True)//设置为守护进程 t.start() ...

  5. python多线程编程

    Python多线程编程中常用方法: 1.join()方法:如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行,那么在调用线程的时就可以使用被调线程的join方法join( ...

  6. Python 多线程教程:并发与并行

    转载于: https://my.oschina.net/leejun2005/blog/398826 在批评Python的讨论中,常常说起Python多线程是多么的难用.还有人对 global int ...

  7. python多线程

    python多线程有两种用法,一种是在函数中使用,一种是放在类中使用 1.在函数中使用 定义空的线程列表 threads=[] 创建线程 t=threading.Thread(target=函数名,a ...

  8. python 多线程就这么简单(转)

    多线程和多进程是什么自行google补脑 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用简单的例子,让你对多线程有个初步的认识. 单线程 在好些年前的 ...

  9. python 多线程就这么简单(续)

    之前讲了多线程的一篇博客,感觉讲的意犹未尽,其实,多线程非常有意思.因为我们在使用电脑的过程中无时无刻都在多进程和多线程.我们可以接着之前的例子继续讲.请先看我的上一篇博客. python 多线程就这 ...

随机推荐

  1. Git在提交代码时出现的fatal: Authentication failed的问题

    git push origin master remote: Incorrect username or password ( access token ) fatal: Authentication ...

  2. Python【day 11】函数名的应用

    函数名的应用 1.函数名字可以作为参数进行传递 2.函数名可以像变量一样进行多次赋值传递,通过print(函数名.__name__)查看原函数 3.函数名表示函数的内存地址 4.函数名()表示函数的执 ...

  3. CTF必备技能丨Linux Pwn入门教程——PIE与bypass思路

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  4. swift混编

    http://blog.csdn.net/fengsh998/article/details/34440159

  5. map、filter、reduce函数的使用

    1.filter() 作用:过滤 // 1.筛选出大于30的数. const array = [10, 20, 30, 40, 50, 60, 70, 80] // 普通写法 // let newar ...

  6. Android不显示开机向导和开机气泡

    修改好的代码下载地址: https://github.com/Vico-H/Launcher 不显示开机向导 修改Launcher2.java的代码 (文件位置: /alps/packages/app ...

  7. MySQL详细安装(windows)

    一.在MySQL官网上下载MySQL Community Server(目前推荐5.7版本,8.0版本在安装的时候遇到的问题比较多) 点击DOWNLOADS→Community→MySQL Commu ...

  8. 从Sources构建nginx,编译安装nginx

    从Sources构建nginx 使用configure命令配置构建,定义了系统的各个方面,包括允许nginx用于连接处理的方法,最后创建了一个Makefile. configure命令参数: --he ...

  9. pytest文档29-allure-pytest(最新最全,保证能搞成功!)

    前言 之前写了个pytest的allure相关的教程,只是停留在环境搭建完成,后续一直没用,小编一直不喜欢这种花里胡哨的报告. 没办法,领导就喜欢这种,小伙伴们也喜欢,所以还是得把allure用起来, ...

  10. LNK1104 无法打开文件“xxx.lib”

    尝试解决方法: 1.找到这个库,把这个库移动到特定的文件夹下,在属性中添加这个库: 具体来说:打开VS项目->项目属性->配置属性->C/C+±>附加包含目录->编辑-& ...