详解Python多线程


Posted in Python onNovember 14, 2016

本文实例为大家解析了Python多线程,供大家参考,具体内容如下

1、多线程的理解

多进程和多线程都可以执行多个任务,线程是进程的一部分。线程的特点是线程之间可以共享内存和变量,资源消耗少(不过在Unix环境中,多进程和多线程资源调度消耗差距不明显,Unix调度较快),缺点是线程之间的同步和加锁比较麻烦。

2、Python多线程创建

在Python中,同样可以实现多线程,有两个标准模块thread和threading,不过我们主要使用更高级的threading模块。使用例子:

import threading
import time
 
def target():
 print 'the curent threading %s is running' % threading.current_thread().name
 time.sleep(1)
 print 'the curent threading %s is ended' % threading.current_thread().name
 
print 'the curent threading %s is running' % threading.current_thread().name
t = threading.Thread(target=target)
 
t.start()
t.join()
print 'the curent threading %s is ended' % threading.current_thread().name

输出:

the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended

start是启动线程,join是阻塞当前线程,即使得在当前线程结束时,不会退出。从结果可以看到,主线程直到Thread-1结束之后才结束。

Python中,默认情况下,如果不加join语句,那么主线程不会等到当前线程结束才结束,但却不会立即杀死该线程。如不加join输出如下:

the curent threading MainThread is running
the curent threading Thread-1 is running
 the curent threading MainThread is ended
the curent threading Thread-1 is ended

但如果为线程实例添加t.setDaemon(True)之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。代码:

import threading
import time
def target():
 print 'the curent threading %s is running' % threading.current_thread().name
 time.sleep(4)
 print 'the curent threading %s is ended' % threading.current_thread().name
print 'the curent threading %s is running' % threading.current_thread().name
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
t.join()
print 'the curent threading %s is ended' % threading.current_thread().name

输出如下:

the curent threading MainThread is running
the curent threading Thread-1 is runningthe curent threading MainThread is ended

如果加上join,并设置等待时间,就会等待线程一段时间再退出:

import threading
import time
def target():
 print 'the curent threading %s is running' % threading.current_thread().name
 time.sleep(4)
 print 'the curent threading %s is ended' % threading.current_thread().name
print 'the curent threading %s is running' % threading.current_thread().name
t = threading.Thread(target=target)
t.setDaemon(True)
t.start()
t.join(1)

输出:

the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading MainThread is ended

主线程等待1秒,就自动结束,并杀死子线程。如果join不加等待时间,t.join(),就会一直等待,一直到子线程结束,输出如下:

the curent threading MainThread is running
the curent threading Thread-1 is running
the curent threading Thread-1 is ended
the curent threading MainThread is ended

3、线程锁和ThreadLocal

(1)线程锁

对于多线程来说,最大的特点就是线程之间可以共享数据,那么共享数据就会出现多线程同时更改一个变量,使用同样的资源,而出现死锁、数据错乱等情况。

假设有两个全局资源,a和b,有两个线程thread1,thread2. thread1占用a,想访问b,但此时thread2占用b,想访问a,两个线程都不释放此时拥有的资源,那么就会造成死锁。

对于该问题,出现了Lock。 当访问某个资源之前,用Lock.acquire()锁住资源,访问之后,用Lock.release()释放资源。

a = 3
lock = threading.Lock()
def target():
 print 'the curent threading %s is running' % threading.current_thread().name
 time.sleep(4)
 global a
 lock.acquire()
 try:
 a += 3
 finally:
 lock.release()
 print 'the curent threading %s is ended' % threading.current_thread().name
 print 'yes'

用finally的目的是防止当前线程无线占用资源。

(2)ThreadLocal

介绍完线程锁,接下来出场的是ThreadLocal。当不想将变量共享给其他线程时,可以使用局部变量,但在函数中定义局部变量会使得在函数之间传递特别麻烦。ThreadLocal是非常牛逼的东西,它解决了全局变量需要枷锁,局部变量传递麻烦的两个问题。通过在线程中定义:

local_school = threading.local()

此时这个local_school就变成了一个全局变量,但这个全局变量只在该线程中为全局变量,对于其他线程来说是局部变量,别的线程不可更改。 def process_thread(name):# 绑定ThreadLocal的student: local_school.student = name

这个student属性只有本线程可以修改,别的线程不可以。代码:

local = threading.local()
def func(name):
 print 'current thread:%s' % threading.currentThread().name
 local.name = name
 print "%s in %s" % (local.name,threading.currentThread().name)
t1 = threading.Thread(target=func,args=('haibo',))
t2 = threading.Thread(target=func,args=('lina',))
t1.start()
t2.start()
t1.join()
t2.join()

从代码中也可以看到,可以将ThreadLocal理解成一个dict,可以绑定不同变量。

ThreadLocal用的最多的地方就是每一个线程处理一个HTTP请求,在Flask框架中利用的就是该原理,它使用的是基于Werkzeug的LocalStack。

4、Map实现多线程

对于多线程的使用,我们经常是用thread来创建,比较繁琐:

class MyThread(threading.Thread):
 def init(self):
 threading.Thread.init(self)
def run(self):
 lock.acquire()
 print threading.currentThread().getName()
 lock.release()
 
def build_worker(num):
 workers = []
 for t in range(num):
 work = MyThread()
 work.start()
 workers.append(work)
 return workers
def producer():
 threads = build_worker(4)
 for w in threads:
 w.join()
 print 'Done'

如果要创建更多的线程,那就要一一加到里面,操作麻烦,代码可读性也变差。在Python中,可以使用map函数简化代码。map可以实现多任务的并发,简单示例:

urls = ['http://www.baidu.com','http://www.sina.com','http://www.qq.com']

results=map(urllib2.urlopen,urls)

map将urls的每个元素当做参数分别传给urllib2.urlopen函数,并最后把结果放到results列表中,map 函数一手包办了序列操作、参数传递和结果保存等一系列的操作。 其原理:

详解Python多线程

map函数负责将线程分给不同的CPU。

在 Python 中有个两个库包含了 map 函数: multiprocessing 和它鲜为人知的子库 multiprocessing.dummy.dummy 是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程。代码:

import urllib2
 
from multiprocessing.dummy import Pool as ThreadPool
 
urls = ['http://www.baidu.com','http://www.sina.com','http://www.qq.com']
 
pool = ThreadPool()
 
results = pool.map(urllib2.urlopen,urls)
print results
pool.close()
pool.join()
 
print 'main ended'

pool = ThreadPool()创建了线程池,其默认值为当前机器 CPU 的核数,可以指定线程池大小,不是越多越好,因为越多的话,线程之间的切换也是很消耗资源的。

results = pool.map(urllib2.urlopen,urls) 该语句将不同的url传给各自的线程,并把执行后结果返回到results中。

代码清晰明了,巧妙得完成Threading模块完成的功能。

5、Python多线程的缺陷:

上面说了那么多关于多线程的用法,但Python多线程并不能真正能发挥作用,因为在Python中,有一个GIL,即全局解释锁,该锁的存在保证在同一个时间只能有一个线程执行任务,也就是多线程并不是真正的并发,只是交替得执行。假如有10个线程炮在10核CPU上,当前工作的也只能是一个CPU上的线程。

6、Python多线程的应用场景。

虽然Python多线程有缺陷,总被人说成是鸡肋,但也不是一无用处,它很适合用在IO密集型任务中。I/O密集型执行期间大部分是时间都用在I/O上,如数据库I/O,较少时间用在CPU计算上。因此该应用场景可以使用Python多线程,当一个任务阻塞在IO操作上时,我们可以立即切换执行其他线程上执行其他IO操作请求。

总结:Python多线程在IO密集型任务中还是很有用处的,而对于计算密集型任务,应该使用Python多进程。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python内置的字符串处理函数整理
Jan 29 Python
python dict remove数组删除(del,pop)
Mar 24 Python
python安装教程 Pycharm安装详细教程
May 02 Python
python虚拟环境virtualenv的安装与使用
Sep 21 Python
pandas将DataFrame的列变成行索引的方法
Apr 10 Python
Python简单实现阿拉伯数字和罗马数字的互相转换功能示例
Apr 17 Python
Python实现的简单排列组合算法示例
Jul 04 Python
np.newaxis 实现为 numpy.ndarray(多维数组)增加一个轴
Nov 30 Python
python numpy 矩阵堆叠实例
Jan 17 Python
python可视化text()函数使用详解
Feb 11 Python
Python如何将图像音视频等资源文件隐藏在代码中(小技巧)
Feb 16 Python
python用分数表示矩阵的方法实例
Jan 11 Python
Python用zip函数同时遍历多个迭代器示例详解
Nov 14 #Python
Python端口扫描简单程序
Nov 10 #Python
Python迭代和迭代器详解
Nov 10 #Python
python通过cookie模拟已登录状态的初步研究
Nov 09 #Python
Python内置函数OCT详解
Nov 09 #Python
windows10系统中安装python3.x+scrapy教程
Nov 08 #Python
简单谈谈python中的多进程
Nov 06 #Python
You might like
php获取本地图片文件并生成xml文件输出具体思路
2013/04/27 PHP
Yii2中使用join、joinwith多表关联查询
2016/06/30 PHP
PHP中类型转换 ,常量,系统常量,魔术常量的详解
2017/10/26 PHP
Js中sort()方法的用法
2006/11/04 Javascript
dess中一个简单的多路委托的实现
2010/07/20 Javascript
JQuery将文本转化成JSON对象需要注意的问题
2011/05/09 Javascript
JavaScript 用cloneNode方法克隆节点的代码
2012/10/15 Javascript
javascript 保存文件到本地实现方法
2012/11/29 Javascript
浅析JavaScript中的隐式类型转换
2013/12/05 Javascript
jquery和javascript中如何将一元素的内容赋给另一元素
2014/01/09 Javascript
jQuery中DOM树操作之复制元素的方法
2015/01/23 Javascript
javascript的变量、传值、传址、参数之间关系
2015/07/26 Javascript
基于jquery步骤进度条源码分享
2015/11/12 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
node.js中的事件处理机制详解
2016/11/26 Javascript
js遍历添加栏目类添加css 再点击其它删除css【推荐】
2018/06/12 Javascript
jQuery-ui插件sortable实现自由拖动排序
2018/12/01 jQuery
微信小程序bindtap事件与冒泡阻止详解
2019/08/08 Javascript
highcharts.js数据绑定方式代码实例
2019/11/13 Javascript
three.js利用射线Raycaster进行碰撞检测
2020/03/12 Javascript
Node.js中出现未捕获异常的处理方法
2020/06/29 Javascript
[01:06:42]VP vs NewBee Supermajor 胜者组 BO3 第二场 6.5
2018/06/06 DOTA
Python数据类型详解(二)列表
2016/05/08 Python
python绘制多个子图的实例
2019/07/07 Python
python next()和iter()函数原理解析
2020/02/07 Python
python3发送request请求及查看返回结果实例
2020/04/30 Python
PyTorch-GPU加速实例
2020/06/23 Python
使用Python实现微信拍一拍功能的思路代码
2020/07/09 Python
python实现简单猜单词游戏
2020/12/24 Python
Gerry Weber德国官网:优质女性时装,德国最大的时装公司之一
2019/11/02 全球购物
党在我心中的演讲稿
2014/09/13 职场文书
教师学习三严三实心得体会
2014/10/13 职场文书
2014年标准化工作总结
2014/12/17 职场文书
工作检讨书大全
2015/01/26 职场文书
如何解决php-fpm启动不了问题
2021/11/17 PHP
python百行代码实现汉服圈图片爬取
2021/11/23 Python