进程介绍和Process
1、什么是进程
- 一个程序运行起来后,它的代码以及需要使用到的资源,称之为进程,它是操作系统分配资源的基本单元,还是线程的容器,一个进程中会有一个或多个线程
- 不仅是可以通过线程来完成多任务操作,进程也是可以的
2、进程的状态
- 工作中,任务数往往会大于CPU的核数。那么肯定有一部分任务正在执行,而另外一部分任务在等待CPU的资源分配,因此就导致进程会有不同的状态
a.就绪状态:运行的条件都已经满足,正在等待CPU的资源分配过来
b.执行状态:CPU正在执行该进程
c.等待状态:在等待某些条件满足。例如:一个程序正在sleep,此时就是等待状态
3、进程、线程进行对比
- 功能
a.进程:能够完成多任务;比如在一台电脑上能够同时运行多个软件
b.线程:能够完成多任务;比如一个浏览器可以开多个窗口 - 定义的不同
a.进程:是操作系统进行资源分配和调度的基本单位
b.线程:线程是进程的一个实体,是CPU进行调度和分派的基本单位。它是比进程更小的能独立运行的单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(比如程序计数器,一组寄存器和栈),但是它可以和同属一个进程之间的其它线程共享该进程所拥有的全部资源 - 区别
a.一个程序至少有一个进程,一个进程至少有一个线程
b.线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高
c.进程在执行过程中拥有独立的内存空间,而该进程的多个线程可以共享这个内存空间,从而极大提高了程序的运行效率
d.不同进程之间的内存空间是独立的
e.线程不能够独立运行,必须依赖于进程 - 优缺点
a.线程的执行开销小,但是不利于资源的管理和保护(线程之间资源共享)
b.进程的执行开销大,但是有利于资源的管理和保护(进程之间资源不共享)
4、multiprocessing
- 官方说明
multiprocessing
是一个用与threading
模块相似API的支持产生进程的包.multiprocessing
包同时提供本地和远程并发,使用子进程代替线程,有效避免Global Interpreter Lock
带来的影响。因此,multiprocessing
模块允许程序员充分利用机器上的多个核心。Unix 和 Windows 上都可以运行。 - Process类
通过创建一个Process
对象然后调用它的start()
方法来生成进程。Process
和threading.Thread
的API相同。
-
Process(group, target, name, args, kwargs)
:
group
:指定进程组,大部分情况使用不到
target
:如果传递了函数的引用,这个子进程就会执行这个函数
name
:给子进程设置名字,可不设置
args
:给target指定的函数传递参数,tuple
kwargs
:给target指定的函数传递参数,dict -
Process
创建的实例对象的常用方法:
start()
:启动子进程实例(创建子进程)
is_alive()
:判断进程的子进程是否还存活
join(timeout)
:是否等待子进程执行结束,或者等待指定的秒数,然后再进行下面的代码
terminate()
:不管任务是否完成,立刻终止子进程
3.Process
创建的实例对象的常用属性:
name
:当前进程的别名,默认为Process-N,N是从1开始递增的整数
pid
:当前进程的pid(进程号),可以用os.getpid()
查看
5、代码例子
- 1、使用进程执行简单任务
import time
from multiprocessing import Process
def work1():
print('喝水5秒')
for i in range(5):
time.sleep(1)
print('喝水中...')
def work2():
print('浇花4秒')
for i in range(4):
time.sleep(1)
print('浇花中...')
""" 使用多进程来执行多任务"""
if __name__ == '__main__': # Process在windows系统运行必须在__main__中,mac则不需要
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
- 2、
Process
执行的注意事项
Process
在windows系统运行必须在__main__
块中,mac则不需要
- 3、创建带参的进程类
方式和Thread
一样,重写__init__
和run
方法
from multiprocessing import Process
class MyPrecess(Process):
def __init__(self, value):
self.value = value
super().__init__()
def run(self):
for i in range(10):
print(F'进程{self.name}的{self.value}在跑第{i}次') # 获取进程名
if __name__ == '__main__':
p = []
p1 = MyPrecess(value='lzl')
p2 = MyPrecess(value='lzl')
p.append(p1)
p.append(p2)
p1.start()
p[0].terminate() # 终止p1
p2.start()
- 4、多进程操作全局变量
1.进程各自拥有自己的变量,不会对全局变量有影响。比如1个主进程中有2个子进程,那么这三个进程都拥有自己的“全局变量”
2.下面例子中,number会有三个值:0、500000、1000000
from multiprocessing import Process
number = 0
def work1():
for i in range(0, 1000000):
global number
number += 1
print(number)
def work2():
for i in range(0, 500000):
global number
number += 1
print(number)
if __name__ == '__main__':
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
print(number)
'''
三个值:
0
500000
1000000
'''
- 5、多进程操作系统资源
1.文件是系统资源,进程之间会共用,并会有资源竞争
2.下面的例子中,跑出来的结果中。文件中的内容并不是1000行java、1000行python
from multiprocessing import Process
def work1():
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('python\n')
def work2():
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('java\n')
if __name__ == '__main__':
p1 = Process(target=work1)
p2 = Process(target=work2)
p1.start()
p2.start()
p1.join()
p2.join()
3.出现这个bug的原因,类似线程对共享资源的竞争,进程函数还在运行时,系统切换了进程资源,导致上一个进程的内容还没完全写入到文件中。
- 6、解决进程之间对系统资源的竞争关系
1.使用锁,对进程进行串行处理。同一时刻只能有一个进程在执行
2.因为是进程,所以锁对象只能通过参数来传递给进程所调用的函数或类
3.使用锁来解决上面例子的bug
from multiprocessing import Process
from multiprocessing import Lock
def work1(lock):
# 操作之前加锁
lock.acquire()
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('python\n')
# 操作结束后释放锁
lock.release()
def work2(lock):
lock.acquire()
for i in range(0, 1000):
with open(r"D:\python\test_09\test\test.txt", 'a') as f:
f.write('java\n')
lock.release()
if __name__ == '__main__':
lock = Lock()
p1 = Process(target=work1, args=(lock,))
p2 = Process(target=work2, args=(lock,))
p1.start()
p2.start()
p1.join()
p2.join()
结果:6、进程之间的通信
1、进程之间的通信使用的也是队列,不过并不是线程所使用的队列
queue.Queue
:进程内的线程之间的队列
multiprocess.Queue
:进程之间的队列2、
multiprocess.Queue
可以使用multiprocess.Queue
来实现多进程之间的数据的传递,Queue本身是一个消息队列程序。
注意:multiprocess.Queue
需要把它的实例对象,以参数的形式传入到各个进程任务中(不会被复制到进程的内存中,只是传递过去),否则各个进程使用的还是各自的队列(也就是把队列复制到各自的内存里了),无法形成进程之间的数据传递
from multiprocessing import Process
from multiprocessing import Queue
import os
q = Queue() # 创建队列
# 给队列添加数据
for i in range(5):
q.put(i)
"""如果不使用传参的方式,那么两个进程之间使用的队列是各自的队列,不能完成通信"""
def work1(q): # 使用同一个队列资源
while not q.empty():
print(F"进程{os.getpid()}在获取队列中的数据{q.get()}")
def work2(q): # 使用同一个队列资源
while not q.empty():
print(F"进程{os.getpid()}在获取队列中的数据{q.get()}")
if __name__ == '__main__':
p1 = Process(target=work1, args=(q,))
p2 = Process(target=work2, args=(q,))
p1.start()
p2.start()
'''
结果:两个进程使用的是同个队列的资源,实现了进程之间的通信
进程8380在获取队列中的数据0
进程8380在获取队列中的数据1
进程8380在获取队列中的数据2
进程8380在获取队列中的数据3
进程8380在获取队列中的数据4
'''