进程
==程序:==例如xxx.py,是一个静态的
==进程:==一个程序运行起来后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单元。不仅可以通过线程完成多任务,进程也是可以的。
进程的状态
工作中,任务数往往大于CPU的核数,即一定有一些任务正在执行,而另外一些任务在等待CPU进行执行,因此导致了有了不同的状态。
进程的创建-multiprocessing
multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。
from multiprocessing import Process
import time
def run_proc():
"""子进程要执行的代码"""
while True:
print("-----2-----")
if __name__ == "__main__"
p = Process(target=run_proc)
p.start()
while True:
print("-----1-----")
time.sleep(1)
说明
import threading
import time
import multiprocessing
def test1():
while True:
print("-----1-----")
time.sleep(1)
def test2():
while True:
print("-----2-----")
time.sleep(1)
def main():
# t1 = threading.Thread(target=test1)
# t2 = threading.Thread(target=test1)
# t1.start()
# t2.start()
p1 = multiprocessing.Process(target=test1)
p2 = multiprocessing.Process(target=test2)
p1.start()
p2.start()
if __name__ == "__main__":
main()
附:线程能实现多任务,进程也能实现,但是进程耗费的资源很大。一个系统上运行的进程数越多,其实占用的资源就越大,运行的效率就越低。所以进程数不是越多越好!
from multiprocessing import Process
import os
import time
def run_proc():
"""子进程要执行的代码"""
print("子进程运行中,PID = %d..." % os.getpid()) # os.getpid 获取当前进程的PID号
time.sleep(1)
print("子进程将要结束...")
if __name__ == "__main__":
print("父进程运行中,PID = %d..." % os.getpid()) # os.getpid 获取当前进程的PID号
p = Process(target=run_proc)
p.start()
功能
定义的不同
区别
优缺点
线程与进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程则相反!
Queue的使用
可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身就是一个消息队列程序,首先用一个小实例来演示一下Queue的工作原理。
示例代码如下:
import multiprocessing
q = multiprocessing.Queue(3) # 初始化一个Queue对象,最多可以接收3条 put 消息
q.put("消息1")
q.put("消息2")
print(q.full()) # False
q.put("消息3")
print(q.full()) # True
# 因为消息队列已满,下面的try都会抛出异常,第一个try会等待2秒后再抛出异常,第二个try会立刻抛出异常
try:
q.put("消息4", True, 2)
except:
print("消息队列已满,现有消息数量:%s" % q.qsize())
try:
q.put_nowait("消息4")
except:
print("消息队列已满,现有消息数量:%s" % q.qsize())
# 推荐的方式,先判断消息队列是否已满,再写入
if not q.full():
q.put_nowait("消息4")
# 读取消息时,先判断消息队列是否为空,再读取
if not q.empty():
for i in range(q.qsize()):
print(q.get_nowait())
import multiprocessing
def download_from_web(q):
"""下载数据"""
# 模拟从网上下载的数据
data = [11, 22, 33, 44]
# 向队列中写入数据
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()
if __name__ == "__main__":
main()
当需要创建的子进程数量不多时,可以直接利用multiprocessing的中Process动态生成多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务,请看下面的实例
代码示例
from multiprocessing import Pool
import os
import time
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(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))
po = Pool(3) # 定义一个进程池,最大进程数:3
for i in range(0, 10):
# Pool().apply_async(要调用的目标, (传递给目标的参数元组,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("-----start-----")
po.close() # 关闭进程池,关闭后po不再接收新的语句
po.join() # 等待po中所有子进程执行完成,必须放在close语句之后
print("-----end-----")
注意:
上文代码在Linux、Mac上是可以正常执行的,但是如果是在Windows系统执行就会抛出RuntimeError异常
经过网络搜集相关信息,得出一个初步的判断,具体对不对我也不知如何验证。这里姑且借前人之言说一下。
造成该异常是因为 Windows系统下在Pycharm 运行 multiprocessing 造成的进程阻塞产生的。
异常信息如下
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
设置函数入口
from multiprocessing import Pool
import os
import time
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(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))
if __name__ == "__main__":
po = Pool() # 定义一个进程池,最大进程数:3
for i in range(0, 10):
# Pool().apply_async(要调用的目标, (传递给目标的参数元组,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("-----start-----")
po.close() # 关闭进程池,关闭后po不再接收新的语句
po.join() # 等待po中所有子进程执行完成,必须放在close语句之后
print("-----end-----")
抛出RuntimeError异常
from multiprocessing import Pool
import os
import time
import random
try:
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(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))
# if __name__ == "__main__":
po = Pool() # 定义一个进程池,最大进程数:3
for i in range(0, 10):
# Pool().apply_async(要调用的目标, (传递给目标的参数元组,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("-----start-----")
po.close() # 关闭进程池,关闭后po不再接收新的语句
po.join() # 等待po中所有子进程执行完成,必须放在close语句之后
print("-----end-----")
except RuntimeError:
print("multiprocessing 在 'Windows'系统下的[RuntimeError]异常,请忽略。")
import os
import multiprocessing
def copy_file(queue, 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完了文件,那么就向队列中写入一个消息,表示已经完成
queue.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的文件夹名字 listdir()
file_names = os.listdir(old_folder_name)
# print(file_names)
# 4.创建进程池
po = multiprocessing.Pool(5)
# 5.创建一个队列
queue = multiprocessing.Manager().Queue()
# 6.向进程池中添加 copy 文件的任务
for file_name in file_names:
po.apply_async(copy_file, args=(queue, file_name, old_folder_name, new_folder_name))
po.close()
# po.join()
all_file_num = len(file_names) # 测一下所有的文件个数
copy_ok_num = 0
while True:
file_name = queue.get()
# print("已经完成COPY:%s" % file_name)
copy_ok_num += 1
print("\rCOPY的进度为:%.2f %% " % (copy_ok_num*100 / all_file_num), end="")
if copy_ok_num >= all_file_num:
break
print()
if __name__ == "__main__":
main()