详解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 相关文章推荐
Linux CentOS7下安装python3 的方法
Jan 21 Python
Python面向对象之接口、抽象类与多态详解
Aug 27 Python
pygame游戏之旅 添加碰撞效果的方法
Nov 20 Python
python单例模式的多种实现方法
Jul 26 Python
python3实现绘制二维点图
Dec 04 Python
Django如何使用jwt获取用户信息
Apr 21 Python
django创建超级用户时指定添加其它字段方式
May 14 Python
如何在sublime编辑器中安装python
May 20 Python
pytorch 常用函数 max ,eq说明
Jun 28 Python
一文读懂Python 枚举
Aug 25 Python
python判断变量是否为列表的方法
Sep 17 Python
Django正则URL匹配实现流程解析
Nov 13 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
德劲1102收音机的打理维修案例
2021/03/02 无线电
PHP parse_url 一个好用的函数
2009/10/03 PHP
phpExcel导出大量数据出现内存溢出错误的解决方法
2013/02/28 PHP
PHP查询网站的PR值
2013/10/30 PHP
十个PHP高级应用技巧果断收藏
2015/09/25 PHP
tp5修改(实现即点即改)
2019/10/18 PHP
js 实现无干扰阴影效果 简单好用(附文件下载)
2009/12/27 Javascript
基于jQuery实现的当离开页面时出现提示的实现代码
2011/06/27 Javascript
鼠标移到图片上变大显示而不是放大镜效果
2014/06/15 Javascript
Javascript基础教程之数据类型 (数值 Number)
2015/01/18 Javascript
使用jQuery实现input数值增量和减量的方法
2015/01/24 Javascript
js制作简易年历完整实例
2015/01/28 Javascript
angularJS提交表单(form)
2015/02/09 Javascript
详解jQuery插件开发方式
2016/11/22 Javascript
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
2016/12/14 Javascript
js操作浏览器的参数方法
2017/01/21 Javascript
解决给dom元素绑定click等事件无效问题的方法
2017/02/17 Javascript
react性能优化达到最大化的方法 immutable.js使用的必要性
2017/03/09 Javascript
纯js实现的积木(div层)拖动功能示例
2017/07/19 Javascript
微信小程序实现底部导航
2018/11/05 Javascript
vue实现Input输入框模糊查询方法
2021/01/29 Javascript
JavaScript制作3D旋转相册
2020/08/02 Javascript
[37:21]完美世界DOTA2联赛PWL S2 Inki vs Magma 第二场 11.22
2020/11/24 DOTA
Scrapy使用的基本流程与实例讲解
2018/10/21 Python
python实现将汉字保存成文本的方法
2018/11/16 Python
Django 开发调试工具 Django-debug-toolbar使用详解
2019/07/23 Python
Win10+GPU版Pytorch1.1安装的安装步骤
2019/09/27 Python
python 操作hive pyhs2方式
2019/12/21 Python
Python基于正则表达式实现计算器功能
2020/07/13 Python
python Tornado框架的使用示例
2020/10/19 Python
移动HTML5前端框架—MUI的使用
2017/12/18 HTML / CSS
税务干部鉴定材料
2014/02/11 职场文书
党员群众路线学习心得体会
2014/11/04 职场文书
党的群众路线教育实践活动心得体会范文
2014/11/05 职场文书
2015年实习生工作总结报告
2015/04/28 职场文书
Spark SQL 2.4.8 操作 Dataframe的两种方式
2021/10/16 SQL Server