目录
引言
一、进程概述
1.1 进程的定义
1.2 进程与程序的区别
二、进程特征
2.1 动态性
2.2 并发性
2.3 独立性
2.4 异步性
2.5 结构特征
三、进程调度
3.1 进程的并行与并发
3.2 进程状态转换
四、同步和异步
4.1 同步
4.2 异步
五、代码实现多进程
5.1 方法一:使用官方提供的进程类
5.2 方法二:创建普通类,继承异常类
六、进程中的常用方法和属性
6.1 常用方法 - join
6.2 常用属性 - daemon
七、总结
在计算机科学的领域中,进程是操作系统运行的核心概念之一。它如同计算机系统中的舞者,在程序的舞台上演绎着各种精彩的运算和交互。无论是多任务处理、资源分配,还是系统的高效运行,进程都扮演着至关重要的角色。本文将深入探讨进程的各个方面,从基本概念到实际的代码实现,帮助读者全面掌握进程这一关键知识。
进程,简单来说,就是正在执行的程序。它不仅仅是一堆静态的指令和数据,更是程序执行过程中的一次动态集合,涵盖了指令、数据集以及相关的运行环境等要素。可以把它看作是程序的一次 “生命旅程”,从开始到结束,在计算机系统中留下运行的轨迹。
在早期面向进程设计的计算机结构里,进程是程序执行的基本实体,承载着程序运行的重任。而在当代面向线程设计的计算机结构中,进程又成为了线程的容器,为线程提供了运行的环境和资源支持。
程序是指令、数据及其组织形式的描述,它是静态的,就像是一本写好的剧本。而进程是程序的实体,是程序在计算机系统中的一次动态执行过程,如同根据剧本在舞台上实际表演的一场戏。一个程序可以对应多个进程,因为同一个程序在不同的数据集上运行,就构成了不同的进程,也会得到不同的运行结果。但在进程执行过程中,程序本身的代码通常是不会发生改变的。
进程的实质是程序在多道程序系统中的一次执行过程。它动态产生,在程序开始运行时被创建;动态消亡,当程序执行完毕或者出现异常终止时,进程也随之结束。这种动态性使得计算机系统能够灵活地响应各种任务需求,根据实际情况创建和销毁进程。
任何进程都可以同其他进程一起并发执行。在多道程序环境下,多个进程可以在宏观上同时运行,虽然在微观上,处理器在某一时刻只能执行一个进程的指令,但通过快速切换,使得用户感觉多个进程好像是同时在运行一样。这种并发性大大提高了计算机系统的资源利用率和整体性能。
进程是一个能独立运行的基本单位。它拥有自己独立的地址空间、资源等,与其他进程相互隔离。同时,进程也是系统分配资源和调度的独立单位,操作系统根据进程的需求来分配 CPU 时间、内存空间、I/O 设备等资源,并对进程的执行进行调度,以确保系统的高效运行。
由于进程间的相互制约,进程具有执行的间断性。各个进程按照各自独立的、不可预知的速度向前推进。例如,一个进程可能在等待 I/O 操作完成时被阻塞,而其他进程则继续执行。这种异步性给操作系统的调度和管理带来了挑战,但也正是它使得计算机系统能够充分利用各种资源,实现高效的多任务处理。
进程由程序、数据和进程控制块(PCB)三部分组成。程序是进程要执行的指令序列;数据是程序执行过程中所需要处理的数据;而进程控制块则是操作系统用于管理进程的核心数据结构,它记录了进程的各种信息,如进程 ID、状态、优先级、资源分配情况等,操作系统通过对 PCB 的操作来实现对进程的控制和管理。
并行是指多个进程在同一时刻真正地同时执行,这需要计算机系统具备多个处理器(多核 CPU),每个处理器可以同时处理一个进程。而并发是指多个进程在一段时间内宏观上同时运行,但在微观上它们是交替执行的,通过处理器的快速切换来实现。在单处理器系统中,进程只能并发执行;而在多处理器系统中,进程既可以并行也可以并发执行。
在程序运行过程中,由于被操作系统的调度算法控制,进程会在几个状态之间转换:
同步是一种任务执行方式,当一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成。这是一种可靠的任务序列,要么两个任务都成功完成,要么都失败,两个任务的状态可以保持一致。例如,在一个数据处理流程中,任务 A 需要先对数据进行预处理,任务 B 才能基于预处理后的数据进行分析,那么任务 B 就必须等待任务 A 完成后才能开始,这就是同步的过程。
异步则与同步相反,不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。比如,在一个网络请求场景中,发起请求的任务不需要等待服务器响应就可以继续执行后续操作,当服务器响应返回时,再通过回调等机制来处理响应结果,这就是异步的操作方式。
在 Python 中,我们可以方便地使用multiprocessing
模块来实现多进程编程。下面介绍两种常见的实现方式。
from multiprocessing import Process
p1 = Process()
print(p1) #
需要注意,进程process
初始化中是需要传参的,在Process
的父类中有默认的初始化函数: def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
pass
参数解释:
target
: 要执行的目标对象(要执行的目标函数)name
: 进程的名称args
: 要执行的目标函数的参数,元组类型def fun(num):
print("这是一个普通方法%s" % num)
p1 = Process(name="路飞", target=fun, args=(1,))
p1.start()
from multiprocessing import Process
def fun(num):
print("这是一个普通方法%s" % num)
if __name__ == '__main__':
# 普通调用
# fun()
# 多进程调用
# 创建进程对象
p1 = Process(name="路飞", target=fun, args=(1,))
p2 = Process(name="乔巴", target=fun, args=(2,))
p3 = Process(name="丹尼", target=fun, args=(3,))
p4 = Process(name="张三", target=fun, args=(4,))
p5 = Process(name="李四", target=fun, args=(5,))
p6 = Process(name="王五", target=fun, args=(6,))
# 执行进程 就绪状态
a = p1.start()
b = p2.start()
c = p3.start()
d = p4.start()
e = p5.start()
f = p6.start()
import os
os.getpid() # 获取进程pid
os.getppid() # 获取父进程的pid
from multiprocessing import Process
class MyProcess(Process):
pass
__init__
和run
方法: class MyProcess(Process):
def __init__(self, n):
super().__init__(name=n)
def run(self):
print("我是子进程")
if __name__ == '__main__':
p1 = MyProcess("路飞")
p1.start()
join
方法可以让父进程等待子进程执行结束,从而使父进程阻塞。正常开启一个子进程时,父进程会等待子进程执行结束,然后父进程再继续执行。例如:
from multiprocessing import Process
import time
def fun():
print("子进程开始")
time.sleep(2)
print("子进程结束")
if __name__ == '__main__':
p1 = Process(target=fun)
p1.start()
print("父进程在子进程开始后立即执行")
p1.join()
print("父进程在子进程结束后执行")
daemon
属性用于设置进程是否为守护进程,必须在start
之前调用。如果一个进程被设置为守护进程,当父进程结束时,子进程也会跟着结束。例如:
from multiprocessing import Process
import time
def fun():
while True:
print("守护进程在运行")
time.sleep(1)
if __name__ == '__main__':
p1 = Process(target=fun)
p1.daemon = True
p1.start()
time.sleep(3)
print("父进程结束")
进程作为操作系统的核心概念,贯穿于计算机系统的运行始终。通过对进程的深入理解,我们掌握了它的定义、特征、调度机制以及在 Python 中的实现方式。无论是进行系统开发、优化程序性能,还是处理复杂的多任务场景,对进程的熟练运用都将为我们提供强大的技术支持。希望本文能够帮助读者在进程的学习道路上迈出坚实的一步,不断探索计算机科学的更多奥秘。