一. 进程是指程序在借用或者占用资源时,被称作为进程
实现代码:
import multiprocessing #导入进程模块
import time
def test1():
while True:
print("----1----")
time.sleep(1)
def test2():
while True:
print("----2----")
time.sleep(1)
def main():
# 调用进程模块中的Process类,目标设置为某一个函数名,创建进程
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
# 执行进程
p1.start()
p2.start()
if __name__ == "__main__":
main()
运行结果:
`
Linux下查看进程的方式:
ps -aux
在一堆进程的下面找到刚刚运行的进程
·
进程分析:
主进程创建第一个进程并运行后:
·
主进程创建第二个进程并运行后:
·
每次创建一个新的进程都会复制一遍所有的代码和数据占用相当多的资源,所以在所有完成多任务的方法中,进程所占用的资源是最大的
拓展:在操作系统中,如果创建进程并运行后不改变原有的代码则可以共享整篇代码,如果通过特殊手段在新的进程中要更改代码,则会原本原样的拷贝一份完整的代码下来进行修改运行
`
二. 关于进程间的通信(队列的使用)
① socket通信:将资源放到网上,不同的进程通过网络共享同一资源,可以实现在不同电脑中也可以进行进程的通信
② 文件:文件存放于硬盘中,通过对文件的读写操作,实现进程间的通信,但是由于文件在硬盘中,导致效率会较低
③ 队列(Queue):创建一个临时内存空间,以“先进先出,后进后出”的规则完成进程间通信
创建队列:
multiprocessing.Queue() # 返回一个队列对象长度不限,括号中可以填写数字限制队列的长度
存数据:
q.put() # 当存满队列时,会一直等待队列释放出新的空间
q.put_nowait() # 当存满队列时,会以报错的形式告诉队列空间已满
取数据:
q.get() # 当队列中的数据全部取出后,会一直等待队列中存入新的数据并取出
q.get_nowait() # 当队列中的数据全部取出后,会以报错的形式告诉队列已空
判断队列的空满:
q.full() # 返回True时队列已满
q.empty() # 返回True时队列已空
代码示例:
import multiprocessing
def download_date(q):
"""下载数据"""
# 假设data为网上下载的数据
data = [11, 22, 33, 44]
for i in data:
q.put(i) # 将数据传到队列中
print("数据全部传输到队列中")
def handle_date(q):
"""数据处理"""
date_list = list() # 定义列表存储队列中的数据
while True:
# 在不清楚队列中有多少数据时用无限循环取出数据
date_list.append(q.get()) # 在队列中取出数据存入列表中
if q.empty():
# 判断当队列为空时跳出循环
break
print(date_list)
def main():
q = multiprocessing.Queue() #创建队列
j1 = multiprocessing.Process(target=download_date, args=(q,))
j2 = multiprocessing.Process(target=handle_date, args=(q,))
j1.start()
j2.start()
if __name__ == "__main__":
main()
注:如果在进程池中用到队列的存取,则需要调用multiprocessing模块中的Manager()类中的Queue()方法,返回一个对象。否则会在进程池中出现异常,但是进程池不会报出异常,导致结果无法达到预期。
·
三. 关于进程池
概念:在开启多进程处理多任务的时候,可以利用Process
动态生成多个进程进行处理,但当对处理对象的数目未知或者成百上千时,利用进程池可以提供指定数量的进程供用户循环使用,当有新的请求提交到Pool(进程池)中,如进程池没满,则会创建一个进程执行要求,如果进程池满了,则该请求会等待,直到池中有进程结束后,才会创建进程执行要求。
·
Pool.apply_async(要调用的目标, (传递给目标的参数元组, )) # 将要执行的对象放入进程池中
代码实例:
# -*- coding:utf-8 -*-
from multiprocessing import Pool
import os, time, random
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d" % (msg, os.getpid()))
time.sleep(random.random()*2)
t_stop = time.time()
print(msg, "执行完毕,耗时%0.2f" % (t_stop-t_start))
p = multiprocessing.Pool(3) # 定义一个进程池,最大进程数3
for i in range(0, 10):
# 每次循环将会用空闲出来的子进程去调用目标
p.apply_async(worker, args=(i,)) # Pool().apply_async(要调用的目标, (传递给目标的参数元组, ))
print("----start----")
p.close() # 关闭进程池,关闭后po不再接收新的请求
p.join() # 等待po进程池中所有进程执行完成后,主线程才继续向下运行
print("----end----")
运行结果:
·
注:这里上面的进程号只有8037,8038,8039三个进程,都是在进程池中的三个进程
注:如果在进程池中产生了异常,程序并不会报出错误
注:在进程池中使用队列,必须使用multiprocessing模块中Manager()类里的Queue()方法
·
实例:用进程池做出文件复制器
import os
import multiprocessing
def copy_file(q, i, file_names, new_file, file_list):
"""复制文件处理"""
open_file = open(file_names+"/"+i, "rb") # 按照路径打开文件并赋予以二进制位读取的权限
content = open_file.read() # 将文件内容保存下来
open_file.close()
new_open_file = open(new_file+"/"+i, "wb")
new_open_file.write(content) # 将文件内容写入
new_open_file.close()
# 复制好后向队列中加入文件数据
q.put(i)
def main():
# 将当前路径下所有目录保存下来
current_file = os.listdir(os.getcwd())
# 获取所要复制的文件夹
file_names = input("请输入要复制的文件夹:")
# 判断输入的目录名是否在当前路径中存在
if file_names in current_file:
try:
# 创建新的文件来接收复制,为防止重复创建报错,抓取异常
new_file = file_names + "[复件]"
os.mkdir(new_file)
except:
pass
# 得到目录下所有文件并以列表的形式保存
file_list = os.listdir(file_names)
# 调用multiprocessing模块中Manager()类里的Queue()方法,返回一个对象,在进程池中使用队列必须使用这种方法
q = multiprocessing.Manager().Queue()
# 获取输入的目录里文件的数量
old_file_num = len(file_list)
# 创建进程池,可同时进行5个进程
po = multiprocessing.Pool(5)
for i in file_list:
po.apply_async(copy_file, args=(q, i, file_names, new_file, file_list))
po.close()
# po.join() ,这里不让主进程等到所有文件都复制好才继续运行
copy_ok = 0
while True:
# 这里必须要取出数据,否则数据文件一直在队列中导致进程池中的队列是满的,从而出现异常,但是在进程池中不会报出异常
file_name = q.get()
# 计数加1
copy_ok += 1
# 这里用"\r"使每次输出左对齐,覆盖上一次输出结果
print("\r完成%.2f %%" % (copy_ok *100 / old_file_num), end="")
if copy_ok >= old_file_num:
break
else:
print("当前目录下没有该文件")
if __name__ == "__main__":
main()
print()