Python多线程即相关理念详解
作者:团子的守护 发布时间:2021-12-07 12:04:47
一、什么是线程?
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程。车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
总结进程与线程区别:
'''
进程:资源单位
线程:执行单位
线程才是真正干活的人,干活中需要的资源由线程所在进程提供
每个进程肯定自带一个线程
每个进程内可创建多个线程
'''
'''
开进程:
申请空间
拷贝代码
消耗资源大
开线程:
同一个进程内创建多个线程,无需上述两种操作,消耗资源相对较小
'''
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
二、开启线程的两种方式
1、方式1
from threading import Thread
import time
# 方法一
def task(name):
print('%s is running' % name)
time.sleep(1)
print('%s is over' % name)
# 开启线程不需要在main下面执行代码,直接书写就可以
# 但是习惯性的将启动命令写在main下面
t = Thread(target=task, args=('egon',))
t.start() # 创建线程的开销非常小,几乎是代码一执行就已经创建了
print('主')
'''
运行结果:
egon is running
主
egon is over
'''
2、方式2
from threading import Thread
class MyThread(Thread):
def __init__(self, name):
# 重写了别人的方法,又不知道别人的方法里有啥,就调用父类的方法
super().__init__()
self.name = name
def run(self):
print('%s is running' % self.name)
time.sleep(1)
print('%s is over' % self.name)
if __name__ == '__main__':
t = MyThread('egon')
t.start()
print('主')
'''
运行结果:
egon is running
主
egon is over
'''
三、线程对象的jion方法()
看过我讲解进程文章的小伙伴想必都知道jion的功能,线程的jion方法于进程的jion方 * 能类似-等待一个线程执行完毕后再执行下一个线程
from threading import Thread
def task(name):
print('%s is running' % name)
time.sleep(1)
print('%s is over' % name)
if __name__ == '__main__':
t=Thread(target=task,args=('egon',))
t.start()
t.join()# 主线程等待子线程运行结束后再执行
print('主')
'''
运行结果:
egon is running
egon is over
主
'''
补充一个知识点:同一个进程下的多个线程数据共享,下面为大家举一个简单的案例
from threading import Thread
money=100
def task():
global money
money=66
if __name__ == '__main__':
t=Thread(target=task,args=())
t.start()
print(money)
# 结果:66
四、 补充小案例
from threading import Thread
import os,time
def task():
print('子 pid:',os.getpid())
if __name__ == '__main__':
t=Thread(target=task,args=())
t.start()
print('主 pid:',os.getpid())
# 两个线程的pid号一样,说明在同一个进程下
'''
运行结果:
子 pid: 13444
主 pid: 13444
'''
# 这是个容易混淆的案例
from threading import Thread,current_thread,active_count
import os,time
def task(n):
print('子',current_thread().name)
time.sleep(n) # 延长线程存活时间
if __name__ == '__main__':
t=Thread(target=task,args=(1,))
t1=Thread(target=task,args=(1,))
t.start()
t1.start()
t.join()
# print('主',current_thread().name)# 获取线程名字
print(active_count()) # 统计当前活跃的进程数
'''
运行结果:
子 Thread-1
子 Thread-2
1
'''
# 这里大家容易以为是3,其实运行后只有一个线程在活跃了,其它两个线程运行完后就停止运行了
五、守护线程
守护线程与守护进程的概念也类似,其实大家也能注意到,进程与线程有许多知识点即用法都是相通的,理解了一个另一个也是差不多的道理
1、守护线程会随着主线程的结束而结束
2、主线程运行结束后不会立刻结束,会等待所有的其它非守护线程结束后才会结束
3、因为主线程的结束意味着所在进程的结束
from threading import Thread
import time
def task(name):
print('%s is running'%name)
time.sleep(1)
print('%s is over'%name)
if __name__ == '__main__':
t=Thread(target=task,args=('egon',))
t.daemon=True #将t设置为守护线程
t.start()
print('主')
'''
运行结果:
egon is running
主
'''
# 稍微有点迷惑性的例子
from threading import Thread
import time
def foo():
print('1234')
time.sleep(1)
print('end1234')
def func():
print('5678')
time.sleep(3)
print('end5678')
if __name__ == '__main__':
t1=Thread(target=foo,args=())
t2=Thread(target=func,args=())
t1.daemon=True # t1设为守护线程,t2为非守护线程
t1.start()
t2.start()
print('主......')
'''
运行结果:
1234
5678主......
end1234
end5678
'''
'''
因主线程会等待非守护线程运行结束后在结束,
所有主线程会等待t2(非守护线程)结束再结束,
'''
六、线程互斥锁
多个线程操作同一份数据的时候,会出现数据错乱的问题
针对上述问题,解决方式就是加锁处理
from threading import Thread,Lock
import time
money=100
mutex=Lock()
def task():
global money
mutex.acquire()
tmp=money
time.sleep(0.1)# 模拟网络延迟
money=tmp-1
mutex.release()
if __name__ == '__main__':
t_list=[]
for i in range(100):
t=Thread(target=task,args=())
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
# 运行结果:0
# 多个人操作同一份数据,数据错乱,加锁处理
七、GTL-全局解释器
相信学python的小伙伴都知道,python解释器其实有多个版本
Cpython
Jpython
Pypython
但是普遍使用的都是Cpython解释器
在Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行
要注意同一进程下的多个线程无法利用多核优势!!!!
想必大家心中也有不少疑惑:pyhon的多线程是不是一点用都没了呢????
因为Cpython中的内存管理不是线程安全的。多线程并不是一无是处的,在遇到多IO操作的时候,多核的优势也会显示不出来,多进程与多线程的效率在该情况下差不了多少,而此时多进程相对浪费资源,多线程更加节省资源
ps:内存管理就是垃圾回收机制:
1、引用计数
2、标记清除
3、分带回收
# GTL-全局解释器
# 重点:1、GIL不是python的特点而是Cpython解释器的特点
# 2、GIL是保证解释器级别的数据的安全
# 3、GIL会导致同一个进程下的多个线程无法同时进行(即无法利用多核优势)
# 4、针对不同的数据还是需要加不同的锁处理
# 5、解释型语言的通病,同一个进程下多个线程无法利用多核优势
多线程是否有用要看具体情况
八、验证多线程与多线程运用场景
# 计算密集型(CPU一直工作,也没有IO)(更适合多进程)
from multiprocessing import Process
from threading import Thread
import os,time
# 多进程情况
def work():
res=0
for i in range(0,10000000):
res*=i
if __name__ == '__main__':
l=[]
print(os.cpu_count())# 获取当前计算机CPU核数
start_time=time.time()
for i in range(8):# 我计算机是8核
p= Process(target=work,args=())
p.start()
l.append(p)
for p in l:
p.join()
print(time.time()-start_time)
'''
运行结果:
8
2.0726492404937744
'''
# 多线程情况
from multiprocessing import Process
from threading import Thread
import os,time
def work():
res=0
for i in range(0,10000000):
res*=i
if __name__ == '__main__':
l=[]
print(os.cpu_count())# 获取当前计算机CPU核数
start_time=time.time()
for i in range(8):# 我计算机是8核
t=Thread(target=work,args=())
t.start()
l.append(t)
for p in l:
p.join()
print(time.time()-start_time)
'''
运行结果:
8
3.5790603160858154
'''
# 显然可知:计算密集型更时候多进程
# IO密集型(任务一直有IO)(多线程更合适)
from multiprocessing import Process
from threading import Thread
import os,time
# 多线程
def work():
time.sleep(1)
if __name__ == '__main__':
l=[]
start_time=time.time()
for i in range(40):
t=Thread(target=work,args=())
t.start()
l.append(t)
for p in l:
p.join()
print(time.time()-start_time)
# 运行结果:1.0205152034759521
# 多进程
from multiprocessing import Process
from threading import Thread
import os,time
def work():
time.sleep(1)
if __name__ == '__main__':
l=[]
start_time=time.time()
for i in range(40):
p= Process(target=work,args=())
# t=Thread(target=work,args=())
# t.start()
# l.append(t)
p.start()
l.append(p)
for p in l:
p.join()
print(time.time()-start_time)
# 运行结果:5.927189588546753
# 显然可知:IO密集型更适合多线程
总结:
多线程和多进程都各自有各自的优势
并且在后面的项目中通常可以多进程下面再开设多线程
这样的话我们可以利用多核也可以节省资源消耗
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
来源:https://blog.csdn.net/m0_51734025/article/details/121597348
猜你喜欢
- 实现效果实现代码import matplotlib.pyplot as pltfrom skimage import iofile_name
- 实例如下所示:#!/usr/bin/python# -*- coding:utf8 -*-from selenium import webd
- 前言要想学好爬虫,必须把基础打扎实,之前发布了两篇文章,分别是使用XPATH和requests爬取网页,今天的文章是学习Beautiful
- 这篇文章主要介绍了Python函数参数类型及排序原理总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 见以下两个文件:showimage.asp Change the HTTP header
- 这里直接给出第一个版本的直接实现:import osimport numpy as npfrom sklearn.cluster impor
- 1、打开指定的网页地址我们使用selenium进行自动化测试时,打开浏览器之后,第一步就是让浏览器访问我们指定的地址,可使用get方法实现f
- min()方法返回它的参数最小值:最接近负无穷大的值。语法以下是min()方法的语法:min( x, y, z, .... )参
- pandas可以非常方便的写数据到excel,那么如何写多个dataframe到不同的sheet呢?使用pandas.ExcelWriter
- 1、目的:在Python中实现只读取扩展名为xlsx的文件解决方法:使用os模块。解决思路:1、确定目录2、循环遍历每一个文件3、筛选符合条
- 运行MySQL Server 5.0安装程序“setup.exe”,出现如下界面: 安装向导启动,按“Next”继续:
- 一、基本结构语句一)、条件语句age = int(input("请输入你家狗狗的年龄: "))print("&
- 定义临时变量:{% with i=1 %}{{i}}{% endwith %}定义对临时变量操作的tag在templatetags中创建se
- 本文实例讲述了PHP+Mysql基于事务处理实现转账功能的方法。分享给大家供大家参考。具体如下:<?php header(
- 简单asp加载access数据库,并生成XML,然后再将XML数据加载进LIST组件范例学习。演示:http://www.taoshaw.c
- 首先,这片文章纯粹是我的个人经验之谈,适用于我常见的环境及项目中。个人建议,数据库字符集尽量使用utf8(HTML页面对应的是utf-8),
- 为了自定义一个模板标签,你需要告诉Django当遇到你的标签时怎样进行这个过程。当Django编译一个模板时,它将原始模板分成一个个 节点
- 最近常有厦门的客户通过网站上的联系方式加我QQ,询问网站改版的情况。几乎每日都要针对客户网站存在的问题做一番分析,然后客户以价格等其他因素结
- 应原书编辑要求,先在文章顶部给出链接:《Everything You Know About CSS Is Wrong》http://www.
- 一、修改操作系统核心参数在Root用户下执行以下步骤:1)修改用户的SHELL的限制,修改/etc/security/limits.conf