程序和进程的概念 p173
进程是运行态的程序
函数式创建子进程
fork可以用于unix类的操作系统:linux,macos
但是在windows系统中,只能使用Process,详见下
第一种创建进程的语法结构:
Process(group=None,target,name,args,kwargs)
参数说明:
1、group:表示分组,实际上不使用,值默认为None即可;可不写
2、target:表示子进程要执行的任务,支持函数名
3、name:表示子进程的名称;可不写
4、args:表示调用函数的位置参数,以元组的形式进行传递;有就写
5、kwargs:表示调用函数的关键字参数,以字典的形式进行传递;有就写
代码实例1:
import multiprocessing
import os
import time
def test():
print(f'我是子进程,我的PID是:{os.getpid()},我的父进程是{os.getppid()}')
time.sleep(1)
if __name__=='__main__':
print('主进程开始执行')
lst=[]
# 创建五个子进程
for i in range(5):
# 创建单个子进程
p=multiprocessing.Process(target=test) # 返回值类型为:<class 'multiprocessing.context.Process'>
print(type(p))
# 启动子进程
p.start()
# 启动中的进程加到列表中
lst.append(p)
print('主进程执行结束')
上面的运行结果是主进程先结束,子进程在逐个结束
若要求所有子进程中的代码执行结束,主进程在结束,可以使用join()方法,阻塞,见下面:
import multiprocessing
import os
import time
def test():
print(f'我是子进程,我的PID是:{os.getpid()},我的父进程是{os.getppid()}')
time.sleep(1)
if __name__=='__main__':
print('主进程开始执行')
lst=[]
# 创建五个子进程
for i in range(5):
# 创建单个子进程
p=multiprocessing.Process(target=test) # 返回值类型为:<class 'multiprocessing.context.Process'>
# 启动子进程
p.start()
# 启动中的进程加到列表中
lst.append(p)
for item in lst:
item.join() # 阻塞主进程
print('主进程执行结束')
Process类常用的属性和方法1 p175
方法/属性名称 | 功能描述 |
---|---|
name |
当前进程实例别名,默认为Process-N
|
pid |
当前进程对象的PID值 |
is_alive() |
进程是否执行完,没执行完结果为True,否则为False |
join(timeout) |
等待结束或传入了参数就是等待timeout秒 |
start() |
启动进程 |
run() |
若没有指定target参数,则启动进程后,会调用父类中的run方法 |
terminate() |
强制终止进程 |
代码实例:
import os,multiprocessing,time
def sub_process(name):
print(f'子进程pid:{os.getpid()},父进程的pid:{os.getppid()},入参name={name}')
time.sleep(1)
def sub_process2(name):
print(f'子进程pid:{os.getpid()},父进程的pid:{os.getppid()},入参name={name}')
time.sleep(1)
if __name__ == '__main__': # 直接写main
# 主进程
print('父进程开始执行')
for i in range(5):
# 创建第一个子进程
p1=multiprocessing.Process(target=sub_process,args=('ysj',))
# 创建第二个子进程
p2=multiprocessing.Process(target=sub_process2,args=(18,))
# 调用Process类的start方法启动子进程
p1.start()
p2.start()
# 调用进程对象的类属性
print(p1.name,'是否执行完毕:',not p1.is_alive())
print(p2.name, '是否执行完毕:', not p2.is_alive())
p1.join() # 主程序阻塞等待p1结束
p2.join() # 主程序阻塞等待p2结束
print('父进程执行结束')
Process类中常用的属性和方法2 p176
代码实例1:
import os,multiprocessing,time
if __name__ == '__main__': # 直接写main
# 主进程
print('父进程开始执行')
for i in range(5):
# 创建第一个子进程
p1=multiprocessing.Process() # 没有给定taget参数,会调用执行Process类中的run方法
# 创建第二个子进程
p2=multiprocessing.Process()
p1.start() # 调用Process类中的run方法去执行
p2.start()
print('父进程执行结束')
代码实例2:
import os,multiprocessing,time
def sub_process(name):
print(f'子进程pid:{os.getpid()},父进程的pid:{os.getppid()},入参name={name}')
time.sleep(1)
def sub_process2(name):
print(f'子进程pid:{os.getpid()},父进程的pid:{os.getppid()},入参name={name}')
time.sleep(1)
if __name__ == '__main__': # 直接写main
# 主进程
print('父进程开始执行')
for i in range(5):
# 创建第一个子进程
p1=multiprocessing.Process(target=sub_process,args=('ysj',)) # 没有给定taget参数,会调用执行Process类中的run方法
# 创建第二个子进程
p2=multiprocessing.Process(target=sub_process2,args=(18,))
p1.start()
p2.start()
# 强制终止进程
p1.terminate()
p2.terminate()
print('父进程执行结束')
继承式创建子进程 p177
第二种创建进程的语法结构:
class 子进程(Process): # 继承Process类,然后去重写run方法
pass
代码实例:
import multiprocessing,time,os
# 自定义一个类
class SubProcess(multiprocessing.Process): # 继承Process类
# 编写一个初始化方法
def __init__(self,name):
# 调用父类的初始化方法
super().__init__()
self.name=name
# 重写父类的run方法
def run(self):
print(f'子进程的名称:{self.name},PID是:{os.getpid()},父进程的PID是:{os.getppid()}')
if __name__ == '__main__':
print('父进程开始执行')
lst=[]
for i in range(1,6):
p1=SubProcess(f'进程:{i}')
# 启动进程
p1.start() # 没有参数会调用run方法
lst.append(p1)
# 阻塞主进程,等待子进程执行完毕
for item in lst:
item.join()
print('父进程执行结束')
进程池的使用 p178
若要创建、管理的进程有上百个,创建、销毁线程要消耗大量的时间。进程池可以解决这个问题
进程池的原理:
创建一个进程池,并设置进程池中最大的进程数量。假设进程池中最大的进程数为3,现在有10个任务需要执行,name进程池一次可以执行3个任务,4次即可完成全部任务的执行。
创建进程池的语法结构:
进程池对象=Pool(N)
方法名 | 功能描述 |
---|---|
apply_async(func,args,kwargs) |
使用非阻塞方式调用函数func |
apply(func,args,kwargs) |
使用阻塞方式调用函数func |
close() |
关闭进程池,不再接受新任务 |
terminate() |
不管任务是否完成,立即终止 |
join() |
阻塞主进程,必须在terminate()或close()之后使用 |
代码实例:非阻塞运行进程池
import multiprocessing,os,time
# 编写任务
def task(name):
print(f'子进程的PID:{os.getpid()},父进程的PID:{os.getppid()},执行的任务:{name}')
time.sleep(1)
if __name__ == '__main__':
# 主进程
start=time.time() # 返回时间戳,单位是秒
print(start,':父进程开始执行')
# 创建进程池
p=multiprocessing.Pool(3)
# 创建任务
for i in range(10):
# 以非阻塞方式
p.apply_async(func=task,args=(i,))
# 关闭进程池不再接收新任务
p.close()
# 阻塞主进程等待子进程执行完毕
p.join()
print(time.time()-start)
print('父进程执行结束')
代码实例:阻塞运行进程池
import multiprocessing,os,time
# 编写任务
def task(name):
print(f'子进程的PID:{os.getpid()},父进程的PID:{os.getppid()},执行的任务:{name}')
time.sleep(1)
if __name__ == '__main__':
# 主进程
start=time.time() # 返回时间戳,单位是秒
print(start,':父进程开始执行')
# 创建进程池
p=multiprocessing.Pool(3)
# 创建任务
for i in range(10):
# 非阻塞方式
p.apply(func=task,args=(i,))
# 关闭进程池不再接收新任务
p.close()
# 阻塞主进程等待子进程执行完毕
p.join()
print(time.time()-start) # 非阻塞用了4秒多,阻塞方式用了10秒多
print('父进程执行结束')
并发和并行 p179
并发:
是指两个或多个事件在 同一时间间隔 发生,多个任务被交替轮换着执行,比如A事件在吃苹果,在吃苹果的过程中有快递员敲门让你收下快递,收快递就是B事件,name收完快递继续吃没吃完的苹果。就是并发。
并行:
指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行。比如A事件是泡脚,B事件是打电话,C事件是记录电话内容,这三件事则可以在同一时刻发生,这就是并行。
进程之间数据是否共享 p180
Python当中的并行对应多进程
代码实例:
import multiprocessing,os
a=100
def add():
print('子进程1开始执行')
global a
a+=30
print('a=',a)
print('子进程1执行完毕')
def sub():
print('子进程2开始执行')
global a
a-=50
print('a=',a)
print('子进程2执行完毕')
if __name__ == '__main__':
# 父进程
print('父进程开始执行')
# 创建加的子进程
p1=multiprocessing.Process(target=add)
# 创建减的子进程
p2=multiprocessing.Process(target=sub())
# 启动子进程
p1.start()
p2.start()
# 主进程阻塞,等待子进程执行完成
p1.join()
p2.join()
print('父进程结束执行')
发现结果分别为 130和50,由此发现多进程之间的数据不是共享的,子进程1中有一份a,子进程2中还有另一份a
如何解决进程之间的数据共享,见下一节
队列的基本使用 p180
进程之间可以通过队列(queue)
进行通信
队列是一种先进先出的数据结构
创建队列的语法结构:
队列对象=Queue(N)
方法名称 | 功能描述 |
---|---|
qsize() |
获取当前队列包含的消息数量 |
empty() |
判断队列是否有空,为空结果为True,否则为False |
full() |
判断队列是否满了,满结果为True,否则为False |
get(block=True) |
获取队列中的一条消息,然后从队列中移除,block默认值为True(队列为空时会阻塞等待消息) |
get_nowait() |
相当于 get(block=False) ,消息队列为空时,抛出异常 |
put(item,block=True) |
将item消息放入队列,block默认为True(队列满时会阻塞等待队列有空间) |
put_nowait(item) |
相当于 put(item,block=False)
|
代码实例:
import multiprocessing
if __name__ == '__main__':
# 创建一个队列
q=multiprocessing.Queue(3) # 这个队列最多可以接收3条信息
print('队列是否有空?',q.empty())
print('队列是否为满?',q.full())
print('队列中的消息数?',q.qsize())
print('-'*88)
# 向队列中添加信息
q.put('hello')
q.put('world')
print('队列是否有空?', q.empty())
print('队列是否为满?', q.full())
print('队列中的消息数?', q.qsize())
print('-'*88)
q.put('11111111')
print('队列是否有空?', q.empty())
print('队列是否为满?', q.full())
print('队列中的消息数?', q.qsize())
print('-' * 88)
print(q.get())
print('队列中的消息数?', q.qsize())
print(q.get())
print(q.get())
print('队列中的消息数?', q.qsize())
'''
队列的遍历:
for i in range(q.qsize()):
q.get_nowait()
'''
使用队列实现进程之间的通信 p182
代码实例1:
import multiprocessing
if __name__ == '__main__':
q=multiprocessing.Queue(3)
# 向队列中添加元素
q.put('hello')
q.put('world')
q.put('python')
q.put('html',block=True,timeout=2) # 阻塞等待,最多两秒,若到了两秒会报错返回
代码实例2:
import multiprocessing,time
a=100
# 入队
def write_msg(q):
global a # 要在函数内使用全局变量,一定要先用此方法声明
if not q.full():
for i in range(6):
a-=10
q.put(a)
print(f'a入队时的值:{a}')
# 出队
def read_msg(q):
time.sleep(1)
while q.qsize()>0:
print(f'出队时a的值:{q.get()}')
if __name__ == '__main__':
print('父进程开始执行')
q=multiprocessing.Queue() # 不写参数表示队列接收的消息个数是没有上限的
# 创建两个子进程
p1=multiprocessing.Process(target=write_msg,args=(q,))
p2=multiprocessing.Process(target=read_msg, args=(q,))
# 启动两个子进程
p1.start()
p2.start()
# 等待写的进程结束,再去执行主进程
p1.join()
p2.join()
print('父进程执行完毕')
函数式创建线程 p183
线程是cpu可调度的最小单位,被包含在进程中,是进程中实际的运作单位。
一个进程可以拥有N多个线程并发执行,而每个线程并行执行不同的任务。
创建线程的方法有两种:函数式创建线程和继承式创建线程
函数式创建线程的语法结构:
t=Thread(group,target,name,args,kwargs)
参数说明:
1、group:创建线程对象的进程组
2、target:创建线程对象所要执行的目标函数
3、name:创建线程对象的名称,默认为 Thread-n
4、args:用元组以位置参数的形式传入target对应函数的参数
5、kwargs:用字典以关键字参数的形式传入target对应函数的参数
代码实例:
import threading,time
# 编写线程执行函数
def test():
for i in range(3):
time.sleep(1)
print(f'线程名:{threading.current_thread().name},正在执行{i}') # 获取当前的线程对象threading.current_thread()
if __name__ == '__main__':
start=time.time()
print('主线程开始执行')
# 线程
lst=[threading.Thread(target=test) for i in range(2)]
for item in lst: # item的数据类型就是Thread类型
# 启动线程
item.start()
for item in lst:
item.join()
print('主线程执行完毕')
print(f'一共耗时{time.time()-start}秒')
# 一共有一个进程,三个线程(一个主线程,两个子线程)
继承式创建线程 p184
使用Thread子类创建线程的操作步骤:
1、自定义类继承threading模块下的Thread类
2、实现run方法
代码实例:
import threading,time
class SubThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
print(f'线程:{threading.current_thread().name}正在执行{i}')
if __name__ == '__main__':
print('主线程开始执行')
# 使用列表生成式去创建线程对象
lst=[SubThread() for i in range(2)]
for item in lst:
item.start()
for item in lst:
item.join()
print('主线程执行完毕')
线程之间数据共享 p185
线程之间的数据可以共享吗?文章来源:https://www.toymoban.com/news/detail-804341.html
import threading
a=100
def add():
print('加线程开始执行')
global a
a+=30
print(f'a的值为{a}')
print('加线程执行完成')
def sub():
print('减线程开始执行')
global a
a-=50
print(f'a的值为{a}')
print('减线程执行完成')
if __name__ == '__main__':
print('主线程开始执行')
print(f'全局变量a的值为{a}')
add=threading.Thread(target=add)
sub=threading.Thread(target=sub)
add.start() # a=130
sub.start() # a=80
add.join()
sub.join()
print('主线程执行完成')
由此可以得到结论:线程之间是可以共享数据的,进程之间不可以共享数据文章来源地址https://www.toymoban.com/news/detail-804341.html
到了这里,关于第13章 1 进程和线程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!