进程操作
进程操作
【一】multiprocessing模块介绍
- python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。
- Python提供了multiprocessing。
- multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
- multiprocessing模块的功能众多:
- 支持子进程、通信和共享数据、执行不同形式的同步
- 提供了Process、Queue、Pipe、Lock等组件。
- 需要再次强调的一点是:
- 与线程不同,进程没有任何共享状
- 进程修改的数据,改动仅限于该进程内。
【二】Process类的介绍
【1】创建进程的类
- 语法
Process([group [, target [, name [, args [, kwargs]]]]])
- 由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
【2】参数介绍
- group参数未使用,值始终为None
- target表示调用对象,即子进程要执行的任务
- args表示调用对象的位置参数元组,args=(1,2,'ly',)
- kwargs表示调用对象的字典,kwargs=
- name为子进程的名称
【3】方法介绍
-
p.start():启动进程,并调用该子进程中的p.run()
-
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
-
p.is_alive():如果p仍然运行,返回True
-
p.join([timeout]):
- 主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。
- timeout是可选的超时时间
- 需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
【4】属性介绍
- p.daemon:
- 默认值为False
- 如果设为True,代表p为后台运行的守护进程
- 当p的父进程终止时,p也随之终止
- 并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
- p.name: 进程的名称
- p.pid:进程的pid
- p.exitcode: 进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
- p.authkey:
- 进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
- 这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
【三】Process类的使用
- 注意:在windows中Process()必须放到
if __name == 'main__':
下
(1)方法一:直接使用Process方法
import multiprocessing
def task(i):
print(f'当前的进程为 :>>>> {i}')
def main_first():
for i in range(1, 5):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
if __name__ == '__main__':
main_first()
# 当前的进程为 :>>>> 1
# 当前的进程为 :>>>> 2
# 当前的进程为 :>>>> 3
# 当前的进程为 :>>>> 4
(2)方法二:继承Process类
import multiprocessing
def task(i):
print(f'当前的进程为 :>>>> {i}')
class MyProcess(multiprocessing.Process):
def __init__(self, i):
super().__init__()
self.i = i
def run(self) -> None:
task(i=self.i)
def main_second():
for i in range(1, 5):
task = MyProcess(i=i)
task.start()
if __name__ == '__main__':
main_second()
# 当前的进程为 :>>>> 1
# 当前的进程为 :>>>> 2
# 当前的进程为 :>>>> 3
# 当前的进程为 :>>>> 4
(3)小结
- 创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
- 一个进程对应在内存中就是一块独立的空间
- 多个进程对应在内存中就是多块独立的内存空间
- 进程与进程之间数据默认情况下是无法直接交互的,如果想交互可以借助第三方工具或模块
【四】进程之间的内存空间是隔离的
- 每一个子进程之间的数据是相互隔离的
- 在执行子进程代码时,只修改自己子进程内的数据,不会影响到其他的子进程
import multiprocessing
a = 9
def task(i):
global a
a += 1
print(f'当前的进程为 {i} :>>>> {a}')
def main_first():
for i in range(1, 5):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
if __name__ == '__main__':
main_first()
# 当前的进程为 1 :>>>> 10
# 当前的进程为 2 :>>>> 10
# 当前的进程为 3 :>>>> 10
# 当前的进程为 4 :>>>> 10
【五】多进程实现TCP服务端并发
服务端
import socket
from socket import SOL_SOCKET, SO_REUSEADDR
import multiprocessing
server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8888))
server.listen()
def talk(conn, addr):
while True:
try:
msg_client = conn.recv(1024)
msg_client = msg_client.decode('utf8')
print(f'这是来自 :>>> {addr} | 信息是 :>>>> {msg_client}')
msg_client = msg_client.upper()
conn.send(msg_client.encode('utf8'))
except Exception as e:
print(f'这是来自 :>>> {addr} | 错误是 :>>>> {e}')
break
def main_multyprocess():
while True:
conn, addr = server.accept()
task = multiprocessing.Process(target=talk, args=(conn, addr))
task.start()
if __name__ == '__main__':
main_multyprocess()
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8888))
while True:
msg = input("请输入小写字母:>>> ").strip()
if not msg: continue
client.send(msg.encode('utf8'))
msg = client.recv(1024)
print(f'这是来自服务端的数据 :>>>>{msg.decode("utf8")}')
【六】Process对象的join方法
- 将并行转为串行
- join:主进程等,等待子进程结束
串行
import multiprocessing,time
def run_task(i):
print(f'{i}开始')
time.sleep(2)
print(f'{i}结束')
def main_first():
a=[]
for i in range(1, 5):
p = multiprocessing.Process(target=run_task, args=(i,))
p.start()
p.join()
if __name__ == '__main__':
start_time = time.time()
main_first()
print(f'这是程序运行的总耗时:{time.time() - start_time}')
# 1
# 开始
# 1
# 结束
# 2
# 开始
# 2
# 结束
# 3
# 开始
# 3
# 结束
# 4
# 开始
# 4
# 结束
# 这是程序运行的总耗时: 8.235054731369019
并行
import multiprocessing,time
def run_task(i):
print(f'{i}开始')
time.sleep(2)
print(f'{i}结束')
def main_first():
a=[]
for i in range(1, 5):
p = multiprocessing.Process(target=run_task, args=(i,))
p.start()
a.append(p)
for i in a:
i.join()
if __name__ == '__main__':
start_time = time.time()
main_first()
print(f'这是程序运行的总耗时:{time.time() - start_time}')
# 1开始
# 2开始
# 3开始
# 4开始
# 4结束3结束
#
# 2结束
# 1结束
# 这是程序运行的总耗时:2.0717782974243164
【六】Process对象的其他方法或属性
【1】什么是进程号
- 一台计算机上面运行着很多进程,那么计算机是如何区分并管理这些进程服务端呢?
- 计算机会给每一个运行的进程分配一个PID号
【2】如何查看进程号?
- Windows系统
- CMD 命令行
tasklist
即可查看
- CMD 命令行
- Mac系统
- 终端运行
ps aux
即可查看
- 终端运行
【3】如何根据指定进程号查看进程
- Mac系统
- 终端运行
ps aux|grep PID
即可查看
- 终端运行
- Windows系统
- CMD 命令行
tasklist |findstr PID
即可查看
- CMD 命令行
【4】方法
查看当前进程的进程号一
- current_process().pid 方法
from multiprocessing import Process, current_process
def task():
print(f'当前程序:>>>>{current_process().pid} 正在运行')
if __name__ == '__main__':
p = Process(target=task)
p.start()
# 当前程序:>>>>1720 正在运行
查看当前进程的进程号二
- os.getpid() 方法
import multiprocessing,os
def task():
print(f'当前程序:>>>>{os.getpid()} 正在运行')
if __name__ == '__main__':
p = multiprocessing.Process(target=task)
p.start()
# 当前程序:>>>>16800 正在运行
查看当前进程的父进程的进程号
- os.getppid() 方法
import multiprocessing,os
def task():
print(f'当前程序:>>>>{os.getpid()} 正在运行')
print(f'当前程序的父进程:>>>>{os.getppid()} 正在运行')
if __name__ == '__main__':
p = multiprocessing.Process(target=task)
p.start()
# 当前程序: >> >> 3308 正在运行
# 当前程序的父进程:>>>>20968 正在运行
杀死当前进程
-
p.terminate()
-
告诉操作系统帮我去杀死当前进程
-
但是需要一定的时间。
-
代码的运行速度极快
-
import multiprocessing,os
def task(i):
print(f'当前进程{i}:>>>>{os.getpid()} 正在运行')
def test():
for i in range(1,5):
p=multiprocessing.Process(target=task,args=(i,))
p.start()
if i==2:
p.terminate()
if __name__ == '__main__':
test()
# 当前进程1:>>>>8872 正在运行
# 当前进程3:>>>>1900 正在运行
# 当前进程4:>>>>15180 正在运行
判断当前进程是否存活
- p.is_alive()
import multiprocessing,os
def task(i):
print(f'当前进程{i} :>>>> {os.getpid()} 正在运行')
def test():
task_list={}
for i in range(1,5):
p=multiprocessing.Process(target=task,args=(i,))
p.start()
if i==2:
p.terminate()
task_list[i]=p
for i,p in task_list.items():
print(f'进程{i}是否存活 :>>> {p.is_alive()}')
if __name__ == '__main__':
test()
# 进程1是否存活 :>>> True
# 进程2是否存活 :>>> False
# 进程3是否存活 :>>> True
# 进程4是否存活 :>>> True
# 当前进程1 :>>>> 20796 正在运行
# 当前进程3 :>>>> 304 正在运行
# 当前进程4 :>>>> 20308 正在运行
【七】僵尸进程和孤儿进程
【1】僵尸进程
-
对于子进程来说:子进程死亡的时候,但是他的这部分资源却没有被回收掉
-
这种现象对于这个死掉的子进程来说就是僵尸进程
【2】孤儿进程
- 子进程对于父进程来说:父进程死亡,子进程也应该跟这个死亡,父进程死了,但是子进程没死,
- init 进程 接收掉所有父进程死亡而子进程未死亡的继承,负责回收掉子进程的资源
【3】僵尸进程的危害大于孤儿进程
- 僵尸进程会占用大部分的资源,并且这部分资源没人来处理
- 孤儿进程虽然父进程没了,但是 init 进程会将这部分资源释放掉
【八】守护进程
【1】什么是守护进程
- 守护进程 (daemon) 是在计算机系统启动时就已经运行,并且一直在后台运行的一类特殊进程。
- 它们通常不与用户直接交互,也不接受标准输入和输出,而是在后台执行某种任务或提供某种服务。
- 守护进程往往是由系统管理员手动启动的,它们可以在系统启动时自动启动,一直运行在后台,直到系统关闭或被停止。
- 常见的守护进程包括网络服务 (如 web 服务器、邮件服务器、 ftp 服务器等)、日志记录系统 (如系统日志服务、应用程序日志服务等) 等。
- 守护进程通常在后台运行,不需要用户交互,并且有较高的权限,因此编写守护进程需要特别注意安全性和稳定性。
【2】主进程死亡,子进程未死亡
from multiprocessing import Process
import time
def task(name):
print(f'总管:>>{name}>>正常存活')
time.sleep(2)
print(f'总管:>>{name}>>正常死亡')
if __name__ == '__main__':
print(f'皇帝 :>>> qc >>> 执掌江山')
p = Process(target=task, args=('aa',))
p.start()
print(f'皇帝 :>>> qc >>> 寿终正寝')
# 皇帝 :>>> qc >>> 执掌江山
# 皇帝 :>>> qc >>> 寿终正寝
# 总管:>>aa>>正常存活
# 总管:>>aa>>正常死亡
【3】主进程死亡,子进程必死亡
from multiprocessing import Process
import time
def task(name):
print(f'总管:>>{name}>>正常存活')
time.sleep(2)
print(f'总管:>>{name}>>正常死亡')
if __name__ == '__main__':
print(f'皇帝 :>>> qc >>> 执掌江山')
p = Process(target=task, args=('aa',))
p.daemon=True
p.start()
print(f'皇帝 :>>> qc >>> 寿终正寝')
# 皇帝 :>>> qc >>> 执掌江山
# 皇帝 :>>> qc >>> 寿终正寝