python基础:多进程、多线程
一、定义和区别
1、一个任务就是一个进程,进程就是资源的集合。比如打开浏览器,启动一个进程。当一个进程需要干很多事的时候,就需要执行多个子任务,这些子任务就是线程。
2、线程是包含在进程中的,每个进程至少有一个默认的线程(主线程),可以有多个线程
3、进程默认有个主线程。而其他子线程则是由主线程启动的。
4、通过线程运行的函数无法return值,如果需要函数返回值,需要专门定义一个list或者字典等来接收
5、多线程运行,一般电脑cpu有几核,就可以同时运行几个线程。但是python中,多线程只能运行一个cpu内核。
因为python中有一个GIL全局解释器,运行python时必须要拿到这把锁才可以执行。遇到I/O操作才会释放。锁只有一把,所以同一时刻也只会有一个线程运行一个cpu内核。那么再写代码运行的时候,为什么有时候多线程看起来确实实在并发,而且比单线程快??请看文章最后
6、多个线程同时修改一个数据时,可能会把数据覆盖。在python2中需要手动加一把锁。但是在python3中会自动加锁
二、多线程
多线程需要引入threading模块:import threading
1、多线程的操作举例:
def run():
time.sleep(3)
print('哈哈哈') for i in range(5):
t=threading.Thread(target=run)#开启一个线程,用target参数指定要运行的函数,target后跟函数名,不加括号。
t.start()#启动线程
运行结果:
从上面的运行结果可以看出。5个线程是同时运行的。
当线程运行的函数需要传递参数时,使用如下:
import threading,time
def run(str):
time.sleep(3)
print('哈哈哈%s:%s'%(time.time(),str)) for i in range(5):
t=threading.Thread(target=run,args=(5,))#用args传递参数。注意的是如果参数只有1个,必须加逗号
t.start()
运行结果:
2、线程等待
比如我们来计算多线程执行的时间,代码如下
import threading,time
def run():
time.sleep(3)
print('哈哈哈哈')
start_time=time.time()#记录开始时间
for i in range(5):
t=threading.Thread(target=run)
t.start()
end_time=time.time()#记录结束时间
print('run_time...',end_time-start_time)#结束时间-开始时间得到运行时间
运行结果:
可以看到,运行时间非常小,这肯定是不正确的。毕竟每个线程运行前都有个time.sleep(3)等待3秒,那么多线程运行的时间必定至少得有3秒。那么上述问题在哪里呢?这就关系到我们最开始说的那个默认主线程了。整个python代码运行时是一个进程,默认的有一个主线程。我们开启的5个线程都是由主线程启动的。当主线程把5个子线程自动以后,它就自动往下运行,执行end_time=time.time()了。而不管子线程的运行是否结束。所以最终这个时间计算的是主线程运行的时间。而不是所有线程执行完的时间。
如果想要计算多线程运行时间,那么就要使用线程等待。让主线程等,等到子线程都运行完了以后,再统计结束时间。。线程等待用join().代码修改如下:
import threading,time
def run():
time.sleep(3)
print('哈哈哈哈') start_time=time.time()
threads=[]#存放启动的五个线程
for i in range(5):
t=threading.Thread(target=run)
t.start()
threads.append(t)#把每个线程都加入到list中保存起来 print('threads,,,,',threads)#可以查看list中保存的5个线程
for t in threads:#主线程循环等待五个子线程
t.join()#等待线程执行结束....虽然是循环挨个等待,但是每个线程都是同时进行的,可能当你等第一个的时候,第二个线程都已经执行完了
#在循环第二个的时候,不用等了直接跳第三个。如果第三四个都执行完了,相当于只等待了第一个执行结束的时间。
#等全部结束了,然后会接着执行下面代码 end_time=time.time()#这个时间就是所有子线程运行结束的时间
print('run_time...',end_time-start_time)
运行结果:
3、守护线程
只要主线程结束,那么子线程立即结束。不管子线程有没有运行完。守护线程的定义:t.setDaemon(True)
示例:
import threading,time
def run():
time.sleep(3)
print('哈哈哈')
for i in range(50):
t=threading.Thread(target=run)
t.setDaemon(True)#把子线程设置成为守护线程,当主线程死了,守护线程也死
t.start() print('done....运行完毕')
运行结果:
可以看到,并没有打印‘哈哈哈’。这是因为,主线程启动50个线程,然后这50个线程执行run函数的时候,都要必须先time.sleep(3)后才会执行print('哈哈哈')’。但是主线程是继续执行的,很快主线程就执行完了,打印‘done....运行完毕’.因为子线程是主线程的守护线程,所以主线程执行完以后,子线程还没来得及打印就跟着主线程一块死了
4、简单的爬虫示例
爬虫就是抓取网页的数据,保存到本地。然后可以对这些数据进行分析。做其他操作
需求:抓取网页数据,并保存到本地为html格式
urls={'':'http://www.11.cn',
'':'http://www.22.cn',
'':'http://www.33.cn',
'':'http://www.44.cn'}#爬虫的网址
dic={}#获取每个网址下载并保存的时间
import requests,time def down_html(filename,url):
start_time=time.time()
req=requests.get(url).content#打开url, .content表示获取url内容返回文件为二进制格式。
open(filename+'.html','wb').write(req)#将抓取到的数据写入一个新的html文件中
end_time=time.time()
print(end_time-start_time,url)
dic[url]=end_time-start_time#用一个字典获取结果 #并行
threads=[]
start_time=time.time()
for k,v in urls.items():#循环5次,5个线程启动, t=threading.Thread(target=down_html,args=(k,v))#多线程传参必须用args传参。target的值是函数名,不加括号
#args如果只有一个参数,必须加一个逗号
t.start()
threads.append(t) for t in threads:#循环等待子线程运行完
t.join() end_time=time.time()
run_time=end_time-start_time
print('下载总共花了%s时间'%run_time)
三、多进程
多进程要引入multiprocessing模块,import multiprocessing
示例:
import multiprocessing,threading
def my():
print('哈哈哈哈')
def run(num): for i in range(num):
t=threading.Thread(target=my)
t.start() if __name__=='__main__':#多进程必须写这个
for p in range(5):
p=multiprocessing.Process(target=run,args=(6,))#进程,args如果只有一个参数,必须加一个逗号
p.start()
四、线程锁
当多个线程需要修改同一个数据时,需要加上线程锁,防止数据被乱改。python2中需要加,python3中不需要。会自动给加
定义一个锁:lock=threading.Lock()
在操作的代码前加锁:lock.aquire()
在操作代码后解锁:lock.release()
示例:
import threading,time
num=1
lock=threading.Lock()
def run():
time.sleep(1)
global num
lock.acqurie()#加锁,python3中不加也可以,会默认加
num+=1
lock.release()#解锁
ts=[]
for i in range(50):
t=threading.Thread(target=run)
t.start()
ts.append(t)
[t.join() for t in ts]#列表生成式,循环等待所有线程结束
print(num)
五、为什么python的多线程不能利用多核CPU,但是咱们在写代码的时候,多线程的确是在并发,而且还比单线程快
原因:因为GIL,python只有一个GIL,运行python时,就要拿到这个锁才能执行,在遇到I/O 操作时会释放这把锁。
如果是纯计算的程序,没有 I/O 操作,解释器会每隔100次操作就释放这把锁,让别的线程有机会 执行(这个次数可以通sys.setcheckinterval
来调整)同一时间只会有一个获得GIL线程在跑,其他线程都处于等待状态
1、如果是CPU密集型代码(循环、计算等),由于计算工作量多和大,计算很快就会达到100,然后触发GIL的释放与在竞争,多个线程来回切换损耗资源,
所以在多线程遇到CPU密集型代码时,单线程会比较快
2、如果是I\O密集型代码(文件处理、网络爬虫),开启多线程实际上是并发(不是并行),IO操作会进行IO等待,线程A等待时,自动切换到线程B,
这样就提升了效率 总结:cpu密集型,单线程快;i/o密集型,多线程快
python基础:多进程、多线程的更多相关文章
- python基础之多线程与多进程(二)
上课笔记整理: 守护线程的作用,起到监听的作用 一个函数连接数据库 一个做守护线程,监听日志 两个线程同时取一个数据 线程---->线程安全---->线程同时进行操作数据. IO操作--- ...
- python基础-12 多线程queue 线程交互event 线程锁 自定义线程池 进程 进程锁 进程池 进程交互数据资源共享
Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...
- python采用 多进程/多线程/协程 写爬虫以及性能对比,牛逼的分分钟就将一个网站爬下来!
首先我们来了解下python中的进程,线程以及协程! 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务.一个CPU,在一个时间切片里只能运行一个程序. 从操作系统的角度: 进程和线程,都 ...
- 也说性能测试,顺便说python的多进程+多线程、协程
最近需要一个web系统进行接口性能测试,这里顺便说一下性能测试的步骤吧,大概如下 一.分析接口频率 根据系统的复杂程度,接口的数量有多有少,应该优先对那些频率高,数据库操作频繁的接口进行性能测试,所以 ...
- python基础之多线程与多进程(一)
并发编程? 1.为什么要有操作系统? 操作系统,位于底层硬件与应用软件之间 工作方式:向下管理硬件,向上提供接口 2.多道技术? 不断切换程序. 操作系统进程切换: 1.出现IO操作 2.固定时间 进 ...
- Python之多进程多线程
一.多进程与多线程的概念 1.多进程的概念 进程是程序在计算机上的的一次执行活动.当你运行一个程序,你就启动了一个进程.显然,程序是死的(静态的),进程是活的(动态的).进程可以分为系统进程和用户进程 ...
- python基础之多线程锁机制
GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器 ...
- python基础===多进程
进程线程的区别在进程,线程,协程的区别 linux或者unix有fork()函数,但是不支持win系统. multiprocessing multiprocessing模块是跨平台版本的多进程模块.支 ...
- python基础之多线程
概念 进程:进程就是一个程序在一个数据集上的一次动态执行过程 程序:代码 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程 ...
- python基础之多线程的操作
# 多线程实例# 例1.单线程from time import sleep,ctimedef task1(taskName): for i in range(2): print("正在执行 ...
随机推荐
- MySQL数据库——连接查询
1.基本含义 连接就是指两个或2个以上的表(数据源)“连接起来成为一个数据源”. 实际上,两个表的完全的连接是这样的一个过程: 左边的表的每一行,跟右边的表的每一行,两两互相“横向对接”后所得到的所有 ...
- C#LeetCode刷题-数组
数组篇 # 题名 刷题 通过率 难度 1 两数之和 C#LeetCode刷题之#1-两数之和(Two Sum) 43.1% 简单 4 两个排序数组的中位数 C#LeetCode刷题之#4-两个排序数组 ...
- ES6语法学习(一)-let和const
1.let 和 const 变量提升: 在声明变量或者函数时,被声明的变量和函数会被提升到函数最顶部: 但是如果声明的变量或者函数被初始化了,则会失去变量提升: 示例代码: param2 = &quo ...
- myBatis源码解析-反射篇(4)
前沿 前文分析了mybatis的日志包,缓存包,数据源包.源码实在有点难顶,在分析反射包时,花费了较多时间.废话不多说,开始源码之路. 反射包feflection在mybatis路径如下: 源码解析 ...
- python基本数据类型(二)
列表 list 1.list.append( p_object) ---- 增加列表参数(向后追加) list=['lifei','liuhua','laochai'] list.append( ...
- JS 时间获取 (常用)
/** * 获取几天之前日期 */ daysAgo(dayNum = 0) { let myDate = new Date() let lw = new Date(myDate - 1000 * 60 ...
- (转)@Autowired(required=false)注入注意的问题
1.前言 在使用spring开发过程中,我们基本上都是使用@Autowired这个注解,用来注入已有的bean.但是有些时候,会注入失败.当我们加上参数(required=false)就能解决.今天整 ...
- springMVC入门(二)------springMVC入门案例
简介 本案例主要完成了springMVC的基本配置,可针对响应的HTTP URL返回数据与视图 一.###web.xml的配置 要使springMVC生效,首先需要对web.xml进行配置,配置spr ...
- 区块链入门到实战(21)之以太坊(Ethereum) – 分布式应用(DApp)
作用:用户交互 分布式应用(DApp)是运行在区块链之上的应用程序,支持区块链网络中用户之间的交互. DApp(decentralized application)的后端代码运行在区块链网络上,这个可 ...
- Istio的流量管理(实操三)
Istio的流量管理(实操三) 涵盖官方文档Traffic Management章节中的egress部分.其中有一小部分问题(已在下文标注)待官方解决. 目录 Istio的流量管理(实操三) 访问外部 ...