第13章 1 进程和线程

news/2024/2/23 15:50:18/文章来源:https://blog.csdn.net/engineer0/article/details/135610858

文章目录

    • 程序和进程的概念 p173
    • 函数式创建子进程
    • Process类常用的属性和方法1 p175
    • Process类中常用的属性和方法2 p176
    • 继承式创建子进程 p177
    • 进程池的使用 p178
    • 并发和并行 p179
    • 进程之间数据是否共享 p180
    • 队列的基本使用 p180
    • 使用队列实现进程之间的通信 p182
    • 函数式创建线程 p183
    • 继承式创建线程 p184
    • 线程之间数据共享 p185

程序和进程的概念 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 timedef 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 timedef 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,timedef 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,timeif __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,timedef 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,osa=100def add():print('子进程1开始执行')global aa+=30print('a=',a)print('子进程1执行完毕')def sub():print('子进程2开始执行')global aa-=50print('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 multiprocessingif __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 multiprocessingif __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,timea=100# 入队
def write_msg(q):global a # 要在函数内使用全局变量,一定要先用此方法声明if not q.full():for i in range(6):a-=10q.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,timeclass 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

线程之间的数据可以共享吗?

import threadinga=100def add():print('加线程开始执行')global aa+=30print(f'a的值为{a}')print('加线程执行完成')def sub():print('减线程开始执行')global aa-=50print(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=130sub.start() # a=80add.join()sub.join()print('主线程执行完成')

由此可以得到结论:线程之间是可以共享数据的,进程之间不可以共享数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_926087.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【docker-compose】【nginx】内网环境https配置

目录 1、openssl生成自签名证书和私钥2、nginx.conf配置ssl3、docker-compose挂载 1、openssl生成自签名证书和私钥 在部署服务器上&#xff0c;新建cert目录&#xff0c;执行以下指令&#xff0c;然后生成.crt和.key文件 openssl req -newkey rsa:2048 -nodes -keyout rsa_pri…

成都力寰璨泓科技有限公司:抖小店新势力

在当下这个信息爆炸的时代&#xff0c;科技的发展日新月异&#xff0c;电商行业更是风起云涌。在这场没有硝烟的战争中&#xff0c;成都力寰璨泓科技有限公司以其敏锐的商业洞察和卓越的技术实力&#xff0c;成功抢滩抖音小店市场&#xff0c;成为一股不可忽视的新生力量。 成都…

ADA-YOLO:YOLOv8+注意力+Adaptive Head,mAP提升3%

生物医学图像分析中的目标检测和定位至关重要&#xff0c;尤其是在血液学领域&#xff0c;检测和识别血细胞对于诊断和治疗决策至关重要。虽然基于注意力的方法在各个领域中目标检测方面取得了显著的进展&#xff0c;但由于医学影像数据集的独特挑战&#xff0c;其在医学目标检…

论文浅尝 | 记忆力强还是健忘?深入探究语言模型的知识记忆机制

笔记整理&#xff1a;方润楠&#xff0c;浙江大学硕士&#xff0c;研究方向为自然语言处理 链接&#xff1a;https://arxiv.org/abs/2305.09144 摘要 近年来&#xff0c;大规模预训练语言模型展示出惊人的记忆能力&#xff0c;相比之下&#xff0c;未经预训练的普通神经网络存在…

Web - Angular 的 AJAX 与 REST

AJAX AJAX 是用于描述网页与网络服务器互动的一系列技术的术语。它不是一项新技术&#xff0c;而是对长时间以来已存在技术的应用。随着主要网站&#xff08;例如谷歌&#xff09;展示其优势&#xff0c;它作为一种开发技术变得流行。AJAX 这个术语被创造出来&#xff0c;用以描…

深度解析JVM类加载器与双亲委派模型

概述 Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心&#xff0c;其中类加载器和双亲委派模型是JVM的重要组成部分。本文将深入讨论这两个概念&#xff0c;并解释它们在实际开发中的应用。 1. 什么是类加载器&#xff1f; 类加载器是JVM的一部分&#xff0c;负…

鸿蒙APP闪退的问题

解决鸿蒙&#xff08;HarmonyOS&#xff09;应用闪退的问题通常需要进行一系列的调查和分析。以下是一些建议的步骤&#xff0c;可以帮助你定位和解决鸿蒙应用闪退的原因&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff…

还在质疑js的性能,人家都干到过第一了。

前两天看到一个 just.js&#xff0c;感到了 Techempower 排名第一&#xff0c;甚至打败了 asp.net core&#xff0c;还有drogn&#xff0c;不知道背后什么原因&#xff0c;反正挺震撼的。 现在开始慢慢验证Atwood定律&#xff1a;任何能够用JavaScript实现的应用系统&#xf…

如何在 Python3 中使用变量

介绍 变量是一个重要的编程概念&#xff0c;值得掌握。它们本质上是在程序中用于表示值的符号。 本教程将涵盖一些变量基础知识&#xff0c;以及如何在您创建的 Python 3 程序中最好地使用它们。 理解变量 从技术角度来说&#xff0c;变量是将存储位置分配给与符号名称或标…

java自动化将用例和截图一起执行测试放入world中直接生成测试报告【搬代码】

1.首先我们得用例写好之后放入文档中&#xff0c;把不用的案例类型、前置条件去掉之后&#xff0c;如图&#xff1a; 放到桌面后&#xff0c;先看执行结果&#xff1a; 首先&#xff0c;我们先创建一个时间&#xff0c;这个时间主要是给图片创建名称&#xff0c;并且要在插入…

【LabVIEW FPGA入门】没有CompactRIO时进行编程测试

1.新建一个空白项目。 2.新建cRIO终端。 要添加仿真的远程实时目标&#xff0c;请选择项目名称&#xff0c;右击并选择新建>>目标和设备(Targets and Devices)。 3.新建终端和设备&#xff0c;选一个cRIO型号 接下来&#xff0c;当添加目标和设备窗口出现时&#xff0c;请…

【数据结构】常见八大排序算法总结

目录 前言 1.直接插入排序 2.希尔排序 3.选择排序 4.堆排序 5.冒泡排序 6.快速排序 6.1Hoare版本 6.2挖坑法 6.3前后指针法 6.4快速排序的递归实现 6.5快速排序的非递归实现 7.归并排序 8.计数排序&#xff08;非比较排序&#xff09; 9.补充:基数排序 10.总结…

Jmeter 性能-监控服务器

Jmeter监控Linux需要三个文件 JMeterPlugins-Extras.jar (包&#xff1a;JMeterPlugins-Extras-1.4.0.zip) JMeterPlugins-Standard.jar (包&#xff1a;JMeterPlugins-Standard-1.4.0.zip) ServerAgent-2.2.3.zip 1、Jemter 安装插件 在插件管理中心的搜索Servers Perform…

散列函数,哈希表hash table

附上一句话&#xff1a;我知道大家可能曾经了解过这个散列表了&#xff0c;我发现&#xff0c;如果多看几个相关的视频&#xff0c;从不同的表述方式和不同的理解角度来理解这个问题&#xff0c;我会明白的更透彻&#xff0c;也有更多新的收获&#xff0c;尤其是对这个算法的应…

宁夏银行关键系统基于OceanBase的创新实践

宁夏银行成立于 1998 年&#xff0c;是宁夏第一家“宁”字号地方商业银行&#xff0c;西部地区第一家以省级行政区命名的地方商业银行。2016 年&#xff0c;被中国人民银行评为宁夏地区系统性重要银行。目前&#xff0c;全行设分支机构 97 家&#xff0c;其中总行营业部 1 家&a…

制造工厂ERP系统:从数字销售-生产到财务管理,掌握企业数字化十大核心!

在快速发展的数字化时代&#xff0c;企业&#xff08;尤其是传统生产制造行业&#xff09;面临着诸多挑战与机遇。无论是客户体验、供应链管理还是内部流程优化&#xff0c;数字化都在发挥着关键作用。为了更好地应对数字化带来的挑战和机遇为了更好地应对市场变化和提高竞争力…

打造高品质家具的必选!数控开料机为何备受推崇?

随着科技的不断进步&#xff0c;数控开料机已经成为了木材加工行业中的首选设备。 一、数控开料机在木材加工行业中的优势 高效、精准的加工效果 数控开料机采用高精度的数控技术和高功率的机械传动系统&#xff0c;可以实现对木材的精确开料和高效加工。与传统的手工操作相…

【RabbitMQ】RabbitMQ高级:死信队列和延迟队列

目录 设置TTL&#xff08;过期时间&#xff09;概述RabbitMQ使用TTL原生API案例springboot案例 死信队列概述原生API案例springboot案例 延迟队列概述插件实现延迟队列安装插件代码 TTL实现延迟队列实现延迟队列优化 设置TTL&#xff08;过期时间&#xff09; 概述 在电商平台…

51单片机-电子密码锁

实物演示效果&#xff1a; https://www.bilibili.com/video/BV1xh4y1K7uV/?vd_source6ff7cd03af95cd504b60511ef9373a1d 电子密码锁的主要功能 1.按键设置6位密码&#xff0c;输入密码若密码正确&#xff0c;则锁打开。显示open&#xff01; 2.密码可以自己修改&#xff0…

Ubuntu 22.04安装使用easyconnect

EasyConnect 百度百科&#xff0c;EasyConnect能够帮助您在办公室之外使用公司内网的所有系统及应用。在您的公司部署深信服远程应用发布解决方案后&#xff0c;您的公司所有业务系统及应用都可以轻松迁移至移动互联网上。您可以通过手机、PAD等智能移动终端随时随地开展您的业…