进程
进程即运行中的程序,进程拥有资源,可以让操作系统调动资源
一个程序可以开启多个进程
进程的状态
1.就绪态:运行的条件都已经满足,正在等待系统执行
2.执行态:cpu正在执行其功能
3.等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
进程的创建
每一个进程的创建都相当于将主进程的资源包括内存再复制一份(代码只有一份,是共享的,只不过主进程和分进程负责的内容不同),然后再运行,线程则都是共享,所以相比于线程,进程消耗的资源比较多
使用multiprocessing模块
例:
import multiprocessing
import time
def test1():
while True:
print("1--------")
time.sleep(1)
def test2():
while True:
print("2--------")
time.sleep(1)
def main():
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
if __name__ == "__main__":
main()
进程和线程对比
进程:能够完成多任务,比如在一台电脑上能运行多个QQ
线程:能够完成多任务,比如一个QQ中的多个聊天窗口,比较轻量级
先有进程才有线程,实质上进程只是一个资源分配的单位,线程是操作系统调度单位,去运行,一个程序中至少有一个主线程。多线程的多任务是在一份资源里面多箭头实现,多进程多任务是复制多份资源多箭头来实现。
线程相当于一个工作台上有多个工人,进程相当于多个工作台。线程执行开销小,但不利于资源的管理和保护,进程正相反
进程间的通信——Queue
可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序
例1:Queue的常用方法
import multiprocessing
q = multiprocessing.Queue(3) # 放3个消息
q.put('111')
q.full() # 判断是否放满 返回False
q.put(2222)
q.put([5, 6, 88, 96])
q.full() # 判断是否放满 返回True
'''此时如果继续put,则程序会堵塞在此处'''
q.get() # '111' 取出,先放进的先取出
q.get() # 2222
q.get() # 列表取出来
'''此时如果继续get,则程序会堵塞在此处'''
q.empty() # 判断是否为空,返回True
例2:进程之间传递数据
import multiprocessing
def download_from_web(q):
'''下载数据'''
# 模拟从网上下载的数据
data = [11, 55, 88, 99]
# 向队列中写入数据
for temp in data:
q.put(temp)
print("---下载器已经下载数据并存入队列---")
def analysis_data(q):
'''数据处理'''
waitting_analysis_data = list() # 和[]等效,更直观一些
# 从队列中获取数据
while True:
data = q.get()
waitting_analysis_data.append(data)
if q.empty():
break
print(waitting_analysis_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()
进程池
当需要创建的子进程不多时,可以直接使用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动创建进程工作量巨大,此时就可以使用multiprocessing模块提供的Pool方法
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool时,如果池还没满,那么就会创建一个新的进程用来执行该请求,如果池中进程数已经到达最大值,则该请求会等待,直至池中有进程结束,才会用之前的进程来执行新任务
例:
import multiprocessing
import time
import os
import random
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d" % (msg, os.getpid()))
# random.random()随机生成0~1之间的浮点数
time.sleep(random.random()*2)
t_stop = time.time()
print("%s执行完毕,耗时%.02f" % (msg, t_stop-t_start))
def main():
po = multiprocessing.Pool(3) # 定义一个进程池,最大进程数为3
for i in range(10):
# Pool().apply_async(要调用的目标,(传递给目标的参数元组,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
po.close() # 关闭进程池
po.join() # 等待po中所有的子进程执行完,必须放在close语句后面
"""如果不添加join,则随着主进程结束,进程池内的程序也不再运行"""
if __name__ == "__main__":
print("-----start-----")
main()
print("-----end-----")
案例:文件夹copy器
import multiprocessing
import os
def copy_file(q, file_name, old_folder_name, new_folder_name):
"""完成文件复制"""
# print("正在copy文件:从%s拷贝至%s,文件名:%s" % (old_folder_name, new_folder_name, file_name))
old_f = open(old_folder_name + "/" + file_name, "rb")
content = old_f.read()
old_f.close()
new_f = open(new_folder_name + "/" + file_name, "wb")
new_f.write(content)
new_f.close()
# 如果copy完了文件,就像队列中写入一个消息
q.put(file_name)
def main():
# 1.获取用户要copy的文件夹名
old_folder_name = input("请输入要copy的文件夹名:")
# 2.创建一个新的文件夹
try:
new_folder_name = old_folder_name + "[附件]"
os.mkdir(new_folder_name)
except:
pass
# 3.获取文件夹中所有待copy文件名 os.listdir()
file_names = os.listdir(old_folder_name)
# print(file_names)
# 4.创建进程池
po = multiprocessing.Pool(5)
# 5.创建队列
'''在Pool中使用Queue要使用multiprocessing下Manage()类下的Queue()方法'''
q = multiprocessing.Manager().Queue()
# 6.向进程池中添加copy文件的人物
for file_name in file_names:
po.apply_async(copy_file, args=(q, file_name, old_folder_name, new_folder_name))
po.close()
''' po.join() 改进代码让copy进度反馈来监控程序完成,
就不需要join()了,因为只有都完成了,主程序才会结束'''
all_file_num = len(file_names) # 所有文件个数
copy_ok_num = 0
while True:
file_name = q.get()
# print("已经完成copy:%s" % file_name)
copy_ok_num += 1
print("\rcopy进度为:%.02f %%" % (copy_ok_num*100/all_file_num), end="")
'''使进度显示在一行变化,显得更高大上一点
重点在于行首增加\r,末尾增加end=""'''
if copy_ok_num >= all_file_num:
break
print("")
if __name__ == "__main__":
main()
>>[out]:请输入要copy的文件夹名:test
copy进度为:100.00 %