python 多线程进度条-线程join() python
前言
多处理库基于线程 API,可以将工作划分为多个进程。 在某些情况下,多进程可以作为临时替代线程来利用多个 CPU 内核,并相应地避免 Python 全局解释器锁导致的计算瓶颈。
接下来,让我们看看多处理库的创建过程与线程库有何相似之处。
创建一个过程
要创建一个进程,最简单的方法就是实例化一个带有目标函数的Process对象,然后像线程一样调用start()函数使其工作。 举例如下:
import multiprocessing
def worker():
for i in range(3):
print(i)
if __name__=="__main__":
p = multiprocessing.Process(target=worker)
p.start()
运行后效果如下:
需要注意的是,Windows上的multiprocessing库创建的进程必须在if __name__=="__main__":中,这就是多进程在Windows上的实现。 在Windows上python 多线程进度条,子进程会自动导入启动它的文件,导入时会执行这些语句。 如果直接创建,会无限递归创建子进程,报错。 因此,创建子进程的部分必须通过if判断进行保护。 导入时__name__不是__main__,不会递归运行。
设置进程名称
在threading线程中,我们可以通过它的参数name来设置线程名,也可以通过name参数来设置进程名。 举例如下:
import multiprocessing
import time
def worker():
print(multiprocessing.current_process().name, "start")
time.sleep(2)
print(multiprocessing.current_process().name, "end")
if __name__ == "__main__":
p1 = multiprocessing.Process(name='p1', target=worker)
p2 = multiprocessing.Process(name='p2', target=worker)
p3 = multiprocessing.Process(name='p3', target=worker)
p1.start()
p2.start()
p3.start()
运行后效果如下:
守护进程
与线程一样,主程序只有在所有子进程都退出后才会退出。 有时,我们可能需要启动一个后台进程,它可以在不阻塞主程序退出的情况下运行。
要标记守护进程python 多线程进度条,可以添加第三个参数daemon,设置为True。 默认值为 False,不充当守护进程。 举例如下:
import multiprocessing
import time
def worker():
print(multiprocessing.current_process().name, "start")
time.sleep(1)
print(multiprocessing.current_process().name, "end")
def worker2():
print(multiprocessing.current_process().name, "start")
time.sleep(2)
print(multiprocessing.current_process().name, "end")
if __name__ == "__main__":
p1 = multiprocessing.Process(name='p1', target=worker)
p2 = multiprocessing.Process(name='p2', target=worker2, daemon=True)
p3 = multiprocessing.Process(name='p3', target=worker2, daemon=True)
p1.start()
p2.start()
p3.start()
运行后效果如下:
p2和p3是daemon进程,而p1不是,所以执行1秒后,主程序退出,p2p3的内容没有打印出来。 但是直到执行完毕,它还在执行中。
加入()
此外,如果您希望强制等待守护进程结束,请添加 join() 函数。 还是上面的代码,例子如下:
import multiprocessing
import time
def worker():
print(multiprocessing.current_process().name, "start")
time.sleep(1)
print(multiprocessing.current_process().name, "end")
def worker2():
print(multiprocessing.current_process().name, "start")
time.sleep(2)
print(multiprocessing.current_process().name, "end")
if __name__ == "__main__":
p1 = multiprocessing.Process(name='p1', target=worker)
p2 = multiprocessing.Process(name='p2', target=worker2, daemon=True)
p3 = multiprocessing.Process(name='p3', target=worker2, daemon=True)
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
运行后和设置进程名的运行结果一样,这里不再展示。 与守护程序代码的唯一区别是最后三行 join() 函数代码。 当然,跟线程一样,你可以传入一个time给join()函数。 过了这个时间,主线程就不会再等待了。
强制结束进程
如果一个进程挂了或者不小心进入了死锁状态,那么这个时候,我们往往会强行结束这个进程。 在进程对象上调用 terminate() 会终止子进程。 举例如下:
import multiprocessing
import time
def worker():
print(multiprocessing.current_process().name, "start")
time.sleep(5)
print(multiprocessing.current_process().name, "end")
if __name__ == "__main__":
p1 = multiprocessing.Process(name='p1', target=worker)
p1.start()
print("是否还在运行", p1.is_alive())
p1.terminate()
print("是否还在运行", p1.is_alive())
p1.join()
print("是否还在运行", p1.is_alive())
运行后输出如下:
终止进程后,使用join()函数等待进程退出。 允许进程管理代码有足够的时间来更新对象的状态以反映进程已终止。
进程退出状态码
当进程退出时,可以通过 exitcode 属性访问生成的状态代码。 下表是状态码的取值范围和含义:
退出代码
意义
没有产生错误
>0
该进程出错并退出并显示此错误代码
该过程以 -1*exitcodde 信号结束
测试如下:
import multiprocessing
import time
def worker():
print(multiprocessing.current_process().name, "start")
time.sleep(5)
print(multiprocessing.current_process().name, "end")
if __name__ == "__main__":
p1 = multiprocessing.Process(name='p1', target=worker)
p2 = multiprocessing.Process(name='p2', target=worker)
p1.start()
p2.start()
print("是否还在运行", p1.is_alive())
p1.terminate()
print("是否还在运行", p1.is_alive())
print(p1.exitcode)
p1.join()
print("是否还在运行", p1.is_alive())
print(p1.exitcode)
time.sleep(5.5)
print(p2.exitcode)
运行后效果如下:
可以看出,强制退出过程的错误码为负数,正常退出过程的错误码为0。
日志
在调试并发问题时,访问多处理提供的对象的内部状态会很有用。 在实际项目中,我们可以使用一个方便的模块级函数启用日志记录,该函数使用日志记录创建一个记录器对象,并添加一个处理程序,使日志消息发送到标准错误通道。
举例如下:
import multiprocessing
import logging
import sys
def worker():
print("运行工作进程")
sys.stdout.flush()
if __name__ == "__main__":
multiprocessing.log_to_stderr(logging.DEBUG)
p1 = multiprocessing.Process(name='p1', target=worker)
p1.start()
p1.join()
运行后效果如下:
分叉过程
和线程一样,我们可以自定义进程,而不是仅仅传入一个函数来创建进程。
创建进程的方法是从进程类派生。 举例如下:
import multiprocessing
class WorkerProcess(multiprocessing.Process):
def run(self):
print(self.name)
return
if __name__ == "__main__":
for i in range(5):
p = WorkerProcess()
p.start()
p.join()
运行后效果如下:
multiprocessing库的进程知识和threading一样长,因为本文内容足够长,剩下的知识会在下一篇博文中讲解。