Hello,大家好。本期来和大家一起学习一下 线程的相关知识。
基础知识补充
64位和32位
32 位和 64 位中的“位”,也叫字长。
字长是 CPU 的主要技术指标之一,指的是 CPU 一次能并行处理的二进制位数
1字节(Byte)=8位(bit/字长/二进制)
64 位的电脑指cpu一次可以处理64个二进制大小的数据
32 位的电脑指cpu一次可以处理32个二进制大小的数据
因此64 位的电脑比32 位的电脑运行速度快。
进程基础知识
概念
进程是操作系统分配资源的基本单位。
一个程序运行起来后,代码与使用到的系统资源被称之为进程。
操作系统工作时,任务数量往往大于cpu的核心数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了进程有三种状态:
就绪态:运行的条件都已经具备,正在等待cpu执行
执行态:cpu正在执行其功能
等待态:等待某些条件满足
进程与线程的不同
进程是分配资源;适用场景:需要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。(CPU密集型)
线程是运行程序;适用场景:主要涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。(IO密集型)
一个进程可以创建多少个线程
这和两个因素有关:
每个线程的栈空间(默认为1M)
一个进程中可用的内存空间(默认为2G)
所以理论上一个进程中最多可以开2048个线程
多进程的使用
多进程
python完成多任务,需要使用python的内置包:multiprocessing
1)创建进程对象:
t1 = multiprocessing.Process(target=sing)
2)运行进程:
t1.start()
3)进程等待:
t1.join()
4)获取当前进程的id:
print(os.getpid())
5)获取当前进程的父进程的id:
print(os.getppid())
多进程案例:
import time
import multiprocessing
def test1(a, b, c, *args, **kwargs):
print(a)
print(b)
print(c)
print(args)
print(kwargs)
def test2():
for i in range(10):
print('222')
time.sleep(1)
def main():
# 1.创建进程对象
"""
在实例化线程对象时,需要指定当前创建的线程对象要运行什么任务?
通过target参数指定一个任务(地址),args以元组的形式传参,kwargs以字典的形式传参。
"""
# multiprocessing.set_start_method('fork') 指定当前进程对象启动方式
p1 = multiprocessing.Process(target=test1, args=(11, 22, 33, 55, 66), kwargs={'name': 'aa'})
p2 = multiprocessing.Process(target=test2)
# 2.内部创建进程并运行
p1.start()
p2.start()
# 如果大家创建的是一个多进程任务,必须写函数入口,不写报错
if __name__ == '__main__':
main()
'''
多进程任务在不同的操作系统上创建的进程方向是不一样的
windows: spawn 必须要写入函数入口
linux: fork 否则指定当前进程对象启动方式
macOS: fork 否则指定当前进程对象启动方式
运行结果:
222
11
22
33
(55, 66)
{'name': 'aa'}
222
222
222
...
'''
特点:和线程执行的顺序都是随机的,进程由操作系统自行调度的。
查看进程号案例:
'''
什么是pid:
一个程序如果在操作系统上被运行之后就变成了一个进程
操作系统如何找到某一个指定的进程
借助pid 相当于给进程分配了一个编号
linus指令:
-查看当前正在运行的进程:ps aux
-查看某个进程:ps aux|grep "python3"
-杀死某一个进程:kill -9 进程号
'''
import os
import time
import multiprocessing
def test():
while True:
# getpid 获取当前进程的id
# getppid 获取当前进程的父进程的id
print('子进程 pid=%d, 父进程 pid=%d' % (os.getpid(), os.getppid()))
time.sleep(1)
def main():
print('主进程 pid=%d 父进程 pid=%d' % (os.getpid(), os.getppid()))
p = multiprocessing.Process(target=test)
p.start()
if __name__ == '__main__':
main()
'''
运行结果:
主进程 pid=33924 父进程 pid=8768
子进程 pid=34300, 父进程 pid=33924
子进程 pid=34300, 父进程 pid=33924
子进程 pid=34300, 父进程 pid=33924
...
'''
多进程存在的问题
多进程之间不共享全局变量-->通过Queue来实现数据共享(解决办法)
不共享全局变量案例:
import time
import multiprocessing
nums = [11, 22, 33]
def test():
nums.append(44)
print('在子进程1中 nums=%s' % str(nums))
time.sleep(3)
def test2():
print('在子进程2中 nums=%s' % str(nums))
def main():
p = multiprocessing.Process(target=test)
p2 = multiprocessing.Process(target=test2)
p.start()
p2.start()
if __name__ == '__main__':
main()
'''
为什么子进程1中获取的全局变量和子进程2中获取的全局变量的值不一致:
因为进程是由主进程将当前的资源(代码)拷贝到子进程中
运行结果:
在子进程1中 nums=[11, 22, 33, 44]
在子进程2中 nums=[11, 22, 33]
'''
通过Queue来实现数据共享案例:
'''
模拟一个场景
进程1 负责下载数据
进程2 处理数据
使用队列进行数据共享 先进先出
# 队列常用的方法:
# q.empty() 判断队列是否是空的
# q.get() 在队列中取值
# q.put() 在队列中放值
# q.full() 判断队列是否是满的
# q.qsize() 返回队列的大小
'''
import multiprocessing
def download_from_web(q):
# 模拟下载的数据
data = [1, 2, 3, 4]
# 向队列写入数据
for i in data:
q.put(i)
print('下载器下载数据完成并存入队列成功')
def analysis_data(q):
# 可以获取数据
waitting_data = list()
while True:
data = q.get()
waitting_data.append(data)
# bool值 如果队列为空 返回true
if q.empty():
break
print(waitting_data)
def main():
# 1. 创建队列
q = multiprocessing.Queue()
# 2. 创建线程
p1 = multiprocessing.Process(target=download_from_web, args=(q,))
p2 = multiprocessing.Process(target=analysis_data, args=(q,))
p1.start()
p2.start()
if __name__ == '__main__':
main()
'''
运行结果:
下载器下载数据完成并存入队列成功
[1, 2, 3, 4]
'''
进程池
学习进程池的原因和线程池是一样的。
我们可以创建一个指定数量的进程池来创建进程,使用到的python第三方库为:
from multiprocessing import Pool
1)创建一个包含3条进程的进程池
po = Pool(3)
2)向进程池提交一个task,i作为worker()函数的参数
po.apply_async(worker, (i,))
3)关闭进程池
po.close()
4)等待进程池中的进程
po.join()
import os
import time
import random
from multiprocessing import Pool
def worker(msg):
# 创建一个时间对象用来进行任务启动的计时
t_start = time.time()
print('%s 开始执行, 进程号: %d' % (msg, os.getpid()))
# 让当前程序随机休眠 0 - 1
time.sleep(random.random() * 2)
t_stop = time.time()
print(msg, '执行完毕, 耗时: %0.2f' % (t_stop - t_start))
def main():
# 创建进程池对象
po = Pool(3) # 如果任务数大于进程数,则多出来的任务会等待被执行
for i in range(10):
# 异步执行的进程任务
po.apply_async(worker, (i,))
# 同步执行的进程任务
# po.apply(worker, (i,)) 一般不用
print('start......')
po.close() # 关闭进程池 当进程池一旦创建并调用close之后 不能动态的去创建进程数量
po.join() # 等待进程池中的子进程执行完毕之后 主进程往下执行
print('end......')
if __name__ == '__main__':
main()
'''
运行结果:
start......
0 开始执行, 进程号: 10120
1 开始执行, 进程号: 33188
2 开始执行, 进程号: 27816
0 执行完毕, 耗时: 1.39
3 开始执行, 进程号: 10120
2 执行完毕, 耗时: 1.49
4 开始执行, 进程号: 27816
1 执行完毕, 耗时: 1.69
5 开始执行, 进程号: 33188
5 执行完毕, 耗时: 0.62
6 开始执行, 进程号: 33188
3 执行完毕, 耗时: 1.74
7 开始执行, 进程号: 10120
6 执行完毕, 耗时: 0.91
8 开始执行, 进程号: 33188
4 执行完毕, 耗时: 1.99
9 开始执行, 进程号: 27816
7 执行完毕, 耗时: 1.11
8 执行完毕, 耗时: 1.04
9 执行完毕, 耗时: 0.87
end......
'''
关于进程的更多用法,欢迎小伙伴后台留言哦。