文章目录
1、 进程的概念
进程(Process)是资源分配的最小单位,也是线程的容器。
进程:一个程序运行起来后,代码+用到的资源称之为进程,他是操作系统分配资源的基本单元。
2、进程的状态
工作中,任务数往往大于CPU的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,应因此导致了不同的状态。
- 就绪态:运行的条件都已经满足,正在等待cup执行
- 执行态:cpu正在执行其功能
- 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
3、进程的创建——multiprocessing
'''
1、导入模块
2、通过模块提供的process类创建进程对象
3、进程对象.start()
'''
# 1、导入模块
import multiprocessing
import time
def work1():
for i in range(10):
print("正在执行work1",i)
time.sleep(0.5)
if __name__ == '__main__':
# 2、通过模块提供的process类创建进程对象
pro_obj=multiprocessing.Process(target=work1)
# 3、进程对象.start()
pro_obj.start()
4、进程名称和pid
获取进程名称:
multiprocessing.current_process()
获取进程编号:
multiprocessing.current_process().pid
导入os模块后: os.getpid()
'''
1、导入模块
2、通过模块提供的process类创建进程对象
3、进程对象.start()
'''
# 1、导入模块
import multiprocessing
import time
import os
def work1():
for i in range(10):
print("子线程名称:",multiprocessing.current_process())
print("子线程id:",multiprocessing.current_process().pid)
print("子线程id:",os.getpid())
#获取进程的父id
print("子线程的id:%s,子线程的父亲id:%s"%(os.getpid(),os.getppid()))
time.sleep(0.5)
if __name__ == '__main__':
#获取主进程的名称
print(multiprocessing.current_process())
#获取进程的编号
print("主进程编号:", multiprocessing.current_process().pid)
#使用os模块获取编号
print("主进程编号:",os.getpid())
# 2、通过模块提供的process类创建进程对象
#target指定子进程要执行的分支函数
#name 指定子进程的名称
pro_obj=multiprocessing.Process(target=work1,name="p1")
# 3、进程对象.start()
pro_obj.start()
5、进程参数、全局变量问题
5.1、子进程参数传递
进程传递参数的方式和线程传递参数的方式基本一致
import multiprocessing
import time
def work1(a,b,c):
print("参数:",a,b,c)
if __name__ == '__main__':
#1)使用args
pro_obj=multiprocessing.Process(target=work1,args=(1,2,3))
# #2)使用kwargs,参数的顺序可以与形参不一致
pro_obj=multiprocessing.Process(target=work1,kwargs={"a":1,"c":2,"b":3})
#混合args 和 kwargs
pro_obj=multiprocessing.Process(target=work1,args=(1,),kwargs={"c":3,"b":2})
pro_obj.start()
5.2、全局变量及共享问题
进程间是不能共享全局变量的
因为:子进程会复制主进程的资源到内部去运行
import multiprocessing
import time
num=0
def work1():
global num
for i in range(10):
num+=1
print("work1-----",num)
def work2():
print("work2-----",num)
if __name__ == '__main__':
pro_obj1=multiprocessing.Process(target=work1)
pro_obj2=multiprocessing.Process(target=work2)
pro_obj1.start()
pro_obj2.start()
time.sleep(1)
print("main------",num)
#结果
work1----- 10
work2----- 0
main------ 0
6、守护进程
通过daemon设置子进程守护主进程
- p1.daemon=True 设置子进程p1守护主进程,当主进程结束的时候,子进程也随之结束
- p1.terminate()终止进程执行,并非是守护进程
import multiprocessing
import time
def work1():
for i in range(10):
print("work1------")
time.sleep(0.5)
if __name__ == '__main__':
pro_obj1=multiprocessing.Process(target=work1)
#守护线程
pro_obj1.daemon=True
pro_obj1.start()
time.sleep(2)
print("主线程结束")
7、线程和进程的对比
进程:能够完成多任务,比如在一台电脑上能够同时运行多个QQ
线程:能够完成多任务,比如一个QQ中的多个聊天窗口
使用区别
- 进程是系统进行资源分配和调度的一个独立单位
- 线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 线程不能独立运行,必须依存在进程中
对比维度 | 多进程 | 多线程 | 总结 |
---|---|---|---|
数据共享、同步 | 数据共享复杂,需要用IPC;数据是分开的,同步简单 | 因为共享进程数据,数据共享简单,但是也因为这个原因导致同步复杂 | 各有优势 |
内存、CPU | 占用内存多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 线程占优 |
创建销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度很快 | 线程占优 |
编程、调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 |
可靠性 | 进程间不会相互影响 | 一个线程挂掉导致整个线程挂掉 | 进程占优 |
分布式 | 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较到简单 | 适用于多核分布式 | 进程占优 |
选择原则:
- 需要频繁创建销毁的优先使用线程(如:web服务器)
- 线程的切换快,所以在需要大量计算,切换频繁时用线程(如图像处理、算法处理)
- 因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布的用线程
- 需要更安全时,适合选择进程;需要速度时,选择进程更好
8、消息队列
可以使用multiprocessing模块中的Queue实现多进程之间的数据传递
Queue本身是一个消息列队程序
8.1、创建队列并且放入值
'''
队列是multiprocessing模块提供的
1)创建队列(指定长度)
2)放值
3)取值
'''
import multiprocessing
# 1)创建队列(指定长度)
#queue=multiprocessing.Queue(n) n表示队列长度
queue=multiprocessing.Queue(5)
# 2)放值
#queue.put(值)
queue.put(1)
queue.put("hello")
queue.put([1,2,3])
queue.put((1,2,3))
queue.put({"a":10,"b":11,"c":12})
#定义的队列长度为5,放入第6个数据后,队列就进行了阻塞状态
# 默认等待队列先取出值,后再放入新的值
# queue.put({"a":11,"b":11,"c":12})
# 3)取值
#queue.get(值)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
#当队列已经空的时候,再次取值,程序会进入阻塞状态,等待放入新的值到队列再取
# value=queue.get()
# print(value)
# print("-"*20)
8.2、消息队列-常见判断
1、判断是否已满
队列.full()
2、判断是否已经为空
队列.empty()
3、取出队列中消息的个数
队列.qsize()
8.3、Queue实现进程间的通信
'''
1、准备两个进程
2、准备一个队列 ,一个进程向队列中写入数据
3、另一个进程进行读数据,就完成进程间的通信
'''
import multiprocessing
import time
#写入数据到队列的函数
def input(queue):
for i in range(10):
if queue.full():
print("队列已满")
break
queue.put(i)
print("%s写入成功"%i)
#读取队列数据并显示的函数i
def output(queue):
while True:
if queue.qsize()==0:
print("队列已空")
break
value=queue.get()
print("取出数据:",value)
if __name__ == '__main__':
#创建一个空的队列
queue=multiprocessing.Queue(3)
#创建两个线程
pro_obj1=multiprocessing.Process(target=input,args=(queue,))
pro_obj2=multiprocessing.Process(target=output,args=(queue,))
pro_obj1.start()
pro_obj1.join()
pro_obj2.start()
9、进程池
9.1、进程池概述
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果数量较多,手动的取创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请;但如果池中的进程数已经达到最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。
'''
1、创建一个函数,用于模拟文件拷贝
2、创建一个进程池,长度3(表示进程池中最多创建三个进程)
3、先用进程池同步方式拷贝文件
4、再用异步的方法拷贝文件
'''
import multiprocessing
import time
# 1、创建一个函数,用于模拟文件拷贝
def copy_work():
print("正在拷贝文件...",multiprocessing.current_process())
time.sleep(1)
# 2、创建一个进程池,长度3(表示进程池中最多创建三个进程)
if __name__ == '__main__':
#创建进程池
pool=multiprocessing.Pool(3)
for i in range(10):
# 3、先用进程池同步方式拷贝文件
# 进程池同步方式pool.apply(函数名,(参数1,参数2...))
#pool.apply_(copy_work)
# 4、再用异步的方法拷贝文件
#如果使用apply_async方式
#1)pool.close()表示不再接收新的任务
#2)主进程不在等待进程池执行结束后再退出,需要进程池join()
#3)pool.join()让主进程等待j进程池执行接收后再退出
pool.apply_async(copy_work)
#pool.close()表示不再接收新的任务
pool.close()
pool.join()
9.2、进程池中的Queue实现进程间的通信
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue()
用法:queue=multiprocessing.Manager().Queue(3)
'''
1、准备两个进程
2、准备一个队列 ,一个进程向队列中写入数据
3、另一个进程进行读数据,就完成进程间的通信
'''
import multiprocessing
import time
def input(queue):
for i in range(10):
if queue.full():
print("队列已满")
break
queue.put(i)
print("队列写入:",i)
def output(queue):
for i in range(10):
if queue.qsize()==0:
print("队列已空")
break
queue.get(i)
print("取出元素:",i)
if __name__ == '__main__':
#创建进程池
pool=multiprocessing.Pool(3)
#c创建进程池中的队列
queue=multiprocessing.Manager().Queue(5)
#使用进程池执行任务
#同步方式
# pool.apply(input,(queue,))
# pool.apply(output,(queue,))
#异步方式
#apply_async()返回值ApplyResult对象,该对象有一个wait()的方法
#wait()方法类似join()表示先让当前进程执行完毕,后续进程才能启动
result=pool.apply_async(input,(queue,))
result.wait()
pool.apply_async(output,(queue,))
pool.close()
pool.join()
10、案例:文件拷贝器
'''
1、定义变量、保存源文件夹、目录文件夹所在的路径
2、在目标路经创建新的文件夹
3、获取源文件夹中的所有文件
4、遍历列表,得到文件夹中的所有文件名
5、定义函数进行函数拷贝
文件拷贝函数:
参数:源文件夹目录,目标文件夹路径、文件名
1、拼接源文件和目录的具体路径
2、打开源文件,创建目录文件
3、读取源文件的内容,写入到目标文件
'''
import os
import multiprocessing
def copy_work(source_dir,dest_dir,file_name):
# 1、拼接源文件和目录的具体路径
# 2、打开源文件,创建目录文件
source_path=source_dir+'/'+file_name
dest_path=dest_dir+"/"+file_name
print(source_path+"----->"+dest_path)
# 3、读取源文件的内容,写入到目标文件
with open(source_path,"rb") as source_file:
#创建目标文件
with open(dest_path,"wb") as dest_file:
while True:
# 读源文件保存到目标文件
file_data = source_file.read(1024)
#判断文件是否读取完
if file_data:
dest_data=dest_file.write(file_data)
else:
break
if __name__ == '__main__':
# 1、定义变量、保存源文件夹、目录文件夹所在的路径
source_dir="其他文件"
dest_dir="C:/Users/haoli/Desktop/test"
# 2、在目标路经创建新的文件夹
try:
os.mkdir(dest_dir)
except Exception as e:
print("文件夹已经存在")
# 3、获取源文件夹中的所有文件
file_list=os.listdir(source_dir)
pool=multiprocessing.Pool(3)
# 4、遍历列表,得到文件夹中的所有文件名
for file_name in file_list:
# 5、定义函数进行函数拷贝
pool.apply_async(copy_work,args=(source_dir,dest_dir,file_name))
pool.close()
pool.join()
#结果
其他文件/1.txt----->C:/Users/haoli/Desktop/test/1.txt
其他文件/2.txt----->C:/Users/haoli/Desktop/test/2.txt
其他文件/3.txt----->C:/Users/haoli/Desktop/test/3.txt
其他文件/4.txt----->C:/Users/haoli/Desktop/test/4.txt
其他文件/5.txt----->C:/Users/haoli/Desktop/test/5.txt
其他文件/6.txt----->C:/Users/haoli/Desktop/test/6.txt