1 多进程概述
进程是正在执行中的应用程序,一个进程包含了该应用程序的所有信息,如加载数据内存空
间、代码、程序数据、对象句柄,执行单元等等,一个应用程序根据其功能的多样性,可以
通过多个进程并发的形式来实现。
计算机中多线程的操作已经可以实现多任务的处理机制了,但是如果涉及到多核 CPU 或者
多个 CPU 的硬件主机,多进程并发编程的实现能比多线程并发机制更加有效的利用和发挥
硬件资源优势。
2 python多进程开发
2.1 multiprocessing
multiprocess常见属性和方法
名称 | 描述 |
---|---|
Process | 进程类型,用于创建和管理进程 |
Lock | RLock |
Event | 进程事件类型,用于进程同步 |
Condition | 进程条件类型,用于进程同步 |
Queue | 进程队列类型,用于多进程数据共享 |
Manager | 进程管理类型,用于多进程数据共享 |
Listener | Client |
2.2 多进程的基础操作
import multiprocessing, time, os
def hello_process():
# 打印信息,展示当前进程编号,父进程编号
print("hello my name is process", os.getpid(),os.getppid())
if __name__ == "__main__":
# 创建一个进程
proc = multiprocessing.Process(target=hello_process)
proc.start()
print("hello, i am main:", os.getpid(), os.getppid())
运行程序,查看运行结果
hello ,i am :2456 2772
hello my name is process 13536 2456
2.3 多进程面向对象实现
多进程的面向对象的实现方式类似多线程的操作模式
自定义进程类型,继承系统进程标准类型 multiprocessing.Process
重写父类的 run()方法,在方法中定义执行代码
在使用时创建该自定义进程类型的对象,调用对象的 start()方法启动一个新的进程
import multiprocessing, os
class MyProcess(multiprocessing.Process):
'''自定义一个进程类型'''
def run(self):
'''重写进程处理方法'''
print("hello, my name is process:", os.getpid(), os.getppid())
if __name__ == "__main__":
print("hello, my name is main:", os.getpid(), os.getppid())
# 创建并启动一个进程
my_proc = MyProcess()
my_proc.start()
2.4 带参数的多进程:共享?独占?
多线程的操作模式下我们的全局变量是多个线程共享的,所以多线程并发模式下对于数据的
修改非常危险,那么多进程模式下数据的处理应该是什么样的呢?
通过两种方式来观察多进程模式下数据的处理
- 全局变量的数据
参数数据
1 全局变量:多进程数据处理
import multiprocessing, time
# 定义全局变量
msg = 3
def chg_numers():
'''定义处理函数,修改全局变量的值'''
global msg
while msg > 0:
msg -= 1
print(multiprocessing.current_process().name, " changed : ", msg)
if __name__ == "__main__":
# 创建两个进程,同时修改数据
for i in range(2):
p = multiprocessing.Process(target=chg_numers)
p.start()
time.sleep(2)
print(multiprocessing.current_process().name, msg)
进程本身就是一个独立运行的程序,多进程意味着当前程序被执行了多次,每个进程中全局变量的数据都是互相独立的。
2 参数传递:多进程数据处理
import multiprocessing, time
def chg_numers(msg):
'''定义处理函数,修改全局变量的值'''
while msg > 0:
msg -= 1
print(multiprocessing.current_process().name, " changed : ", msg)
if __name__ == "__main__":
# 创建两个进程,同时修改数据
msg = 3
for i in range(2):
p = multiprocessing.Process(target=chg_numers, args=(msg,))
p.start()
time.sleep(2)
print(multiprocessing.current_process().name, msg)
给多进程并发处理函数传递参数的方式,并不能让数据可以被多个进程共享
函数执行并发操作时,每个进程都会单独拷贝一份当前进程的变量数据进行独立使用而不互
相影响,这也是出现上述代码结果的原因
2.5 多进程的简化:内置进程池
多进程的操作在实际应用中也是非常多的,但是纯底层的代码开发控制并发也是一件非常繁
琐的事情,所以就出现了面向过程多进程并发的优化操作方式:进程池 Pool
通过进程池 Pool 可以快速创建多个进程执行指定函数,完成高并发处理操作
1. Pool对象的属性和方法
名称 | 描述 |
---|---|
apply(func, args) | 传递参数 args 并执行函数 func,同时阻塞当前进程直到该函数执行完成,函数 func 只会在进程池中的一个进程中运行 |
close() | Pool 进程池的底层工作机制是向进程池提交任务产生工作进程执行,该方法是主动停止给进程池提交任务,并等待所有提交任务执行完成退出 |
terminate() | 立即结束该进程,当进程池对象被回收时自动调用该方法 |
join() | 等待工作进程退出,再次之间必须调用 close()或者 teminate |
进程池的基本实现
import multiprocessing
def my_process():
print("hello my name is : ", multiprocessing.current_process().name)
if __name__ == "__main__":
# 创建一个进程池对象,该进程池可以产生两个处理进程
pool = multiprocessing.Pool(2)
# 定义 8 个任务,交给进程池处理
for i in range(8):
pool.apply_async(my_process)
# 停止提交任务
pool.close()
# 独占模式:让主线程等待进程池任务执行完成
pool.join()
可以看到两个进程池产生了两个 工作进程来处理我们的循环的8个任务
多进程下载
有了进程池,可以简单完成一个多进程任务下载的操作处理
该案例只是模拟多进程处理过程,下载任务数据的代码请参考网络数据爬虫技术
import multiprocessing, time
def download(url):
print(multiprocessing.current_process().name, "开始下载..")
time.sleep(0.5)
print(multiprocessing.current_process().name,"下载完成<<")
time.sleep(2)
return "下载的数据"
def savedata(data):
print(multiprocessing.current_process().name,"下载的数据")
if __name__ == "__main__":
# 创建进程池
p = multiprocessing.Pool(5)
# 任务下载循环
for i in range(20):
p.apply_async(download, args=("http://www.dy2018.com",), callback=savedata)
# 停止提交任务
p.close()
# 独占
p.join()
执行时,可以看到20个任务被进程池中的5个进程平均分配进行了处理
2.6 多个进程通信:multiprocessing.Manager
不同线程之间的数据通信,涉及到核心的数据共享问题,主要由PYTHON中提供了内建模块multiprocessing.Manager类型实现
multiprocessing.Manager常见属性和方法
名称 | 描述 |
---|---|
Array | 内置进程间共享数组类型 |
Queue | 内置进程间共享队列类型 |
list() | 内置进程间共享列表类型 |
dict() | 内置进程间共享字典类型 |
Value | 内置进程间共享值类型 |
Barrier | 进程同步类型 |
BoundedSemaphore、Semaphore | 进程信号量类型 |
Lock | RLock |
Event | 进程同步事件类型 |
Condition | 进程同步条件类型 |
2.7 多个进程通信:multiprocessing.Queue
多个进程之间的通信操作,数据的传递在PYTHON中的multiprocessing模块中提供了一个专门用于多进程之间进行数据传递的队列:Queue
multiprocessing.Queue 常见属性和方法
名称 | 描述 |
---|---|
put(data [, timeout=None]) | 添加一个数据到队列中 |
put_nowait(data) | 添加一个数据到队列中,非阻塞模式 |
get([timeout=None]) | 从队列中获取一个数据 |
get_nowait() | 从队列中获取一个数据,非阻塞模式 |
full() | 判断队列是否已满 |
empty() | 判断队列是否已空 |
close() | 关闭队列 |
qsize() | 获取队列中的元素数量 |
2.8 多个进程通信:multiprocessing.Pipe
PYTHON 为了更加友好的多个进程之间的数据通信操作,提供了一个管道类型专门用于进程之间的协作:mulriprocessing.Pipe
multiprocessing.Pipe常见属性和方法
名称 | 描述 |
---|---|
init(duplex=True) | 初始化方法,返回两个数据 conn1,conn2,分别表示管道的两端,默认是双向通信。如果 duplex=False,conn1 只能接受消息,conn2 只能发送消息 |
send(data) | 发送消息 |
recv() | 接受消息 |