2020-08-14 多任务执行之进程

多任务之进程

一 多任务的概念

   指在同一时间内同时执行多个任务. 例如 可以同时运行微信和QQ

 

二 多任务执行方式

  1并发:指单核CPU 执行任务 .  操作系统让各个软件交替执行. 由于CPU执行速度太快,让我们感觉是在同时运行的.

  2并行:指多核CPU执行任务,操作系统会给每个内核安排一个软件进行执行.是真正一起执行软件的.

 

三 多进程方式完成多任务

  1进程的概念 : 一个正在运行的程序或者软件就是一个进程.进程是操作系统分配资源的基本单位.  即每启动一个进程,操作系统就会给其分配一定的运行资源(内存资源)保证进程的运行。

  2创建两个子继承,并将各自任务交给他们,启动进程,就能执行多任务.主进程: 直接执行的进程. 子进程:手动创建的进程叫子进程

  3 多进程执行多任务流程:

  • 3.1 导入multiprocessing进程包和time模块(time模块作用是延迟时间,观察代码执行状态)
  • 3.2 创建任务(指函数或者类),最好是循环任务,使我们可以更加清晰的看出代码执行状态
  • 3.3 加入 if __name__ == '__main__': 判断代码
  • 3.4 将任务交给子进程处理
  • 3.5 创建子进程 变量名 = multiprocessing.Process(group=None,target =任务名,name,args ,kwargs )
    • group 指定进程组 目前只能使用None
    • target = 任务名 name = 进程名字(默认为Process-1-N)
    • args 元组数据传参
    • kwargs 字典数据进行传参
  • 3.6 启动子程序  变量名.strat()

  4 进程执行是无序的,具体哪个先执行是由CPU调度决定的

5 结论:

通过观察,我们发现两个任务可以同时进行了,这样就使用进程方式完成了多任务执行.

四.获取多任务编号

1.获取进程编号作用: 验证子进程和主进程的关系.得知子进程是由哪个主进程创建的.

2.获取进程编号的两种操作方法:

  • os.getpid() 获取当前进程编号
  • os.getppid() 获取当前进程父进程编号
  • multiprocessing.current_process() 获取当前进程对象

3.代码执行流程如下:

  • 3.1在多进程执行多任务的基础上书写代码
  • 3.2导入 os模块
  • 3.3获取主进程(即直接执行的进程)进程编号和进程名称,并格式化输出print('mian_id',os.pid,multiprocessing.current_process() )
  • 3.4 获取子进程(在任务中)进程编号,进程名称print('sing_id',os.pid,multiprocessing.current_process() )
  • 3.5 获取子进程的父进程编号 print('sing_parent_id',os.ppid)
  • 3.6 观察父进程编号和主进程编号
  • 3.7 os.kill(进程编号,9)根据进程编号强制杀死该进程.

4.结论:

子进程的父进程编号和主进程编号相同,证明子进程是由主进程创建的.

五.进程执行带参数的多任务

1 创建任务带有参数,就要在进程处传入实参

2 args = () 第一种传参方法,元组传参,args是一个位置参数

3 kwargs ={} 第二章传参方法,字典传参,字典的key值要和函数的参数名保持一致,是关键字参数

4 args 和 kwargs 一起执行

sub_process = multiprocessing.Process(target=info_show,args = ('张三',), kwargs={'age' : 20 })

六 进程的注意点

1 进程之间不共享数据

①代码验证流程如下:

  • 分析: 设置两个进程,一个添加数据,另一个读取数据,观察结果
  • 1.1 导入multiprocessing 包 和time模块
  • 1.2 设置全局变量,一个列表
  • 1.3 创建第一个任务:向列表中添加数据(格式化输出每次打印的数据)
  • 1.4 创建第二个任务:读取列表
  • 1.5 创建两个子进程,分别将两个任务交给进程
  • 1.6 启动进程
  • 1.7 为了效果明显,可使用join()函数 等待添加函数结束后再执行读取函数
  • 结论: 可以看出两个子进程之间是不共享全局变量的

②原理分析:

  • 2.1 主进程创建子进程的过程,实质是子进程拷贝主进程的数据资源,相当于副本

所以两个进程是互不干涉的,其中一个操作全局变量也就和另一个进程没有关系.

③ Windows系统会无限递归创建子进程.解决办法: 通过判断是否是主模块

if __name__ == '__main__':

作用1: 防止别人导入文件的时候执行里面的代码

作用2: 防止Windows系统中递归创建子进程.

 

2 主进程要等待子进程执行结束再结束

①代码执行流程如下:

  • 2.1 导入multiprocessing包和time模块
  • 2.2 创建任务,
  • 2.3 将任务交给子进程
  • 2.4 主进程启动
  • 2.5 控制时间 子进程的完成时间要多于主进程时间
  • 2.6 观察结果
  • 结论: 主进程会等待子进程结束再结束

② 解决办法:

1. 设置为子进程守护主进程,主进程结束后,子进程会被销毁(在start之前设置)

sub_process.daemon = True

2. 销毁子进程

sub_process.terminate()

 

多任务之线程

一.线程的介绍

1.概念:线程是进程中执行代码的一个分支,每个分支(线程)想要执行代码需要CPU进行调度.线程是CPU调度的基本单位. 每个进程至少有一个线程,就是主线程.

2.多线程完成多任务

 

 

二.多线程执行多任务

①代码执行流程如下:

  • 2.1 导入threading线程模块和time模块
  • 2.2 创建任务,查看当前线程对象 threading.current_thread()
  • 2.3 if __name__ == '__main__': 判断
  • 2.4 创建子线程,将任务交给子线程执行 threading.Thread()(参数与进程相同)
  • 2.5 启动线程

②多线程传入参数 args : 元组传参. kwargs: 字典传参

三.线程注意点

3.1 线程执行是无序的

代码执行流程如下:

  • 导入线程模块
  • 创建大量子线程 for循环创建
  • 创建任务,延时1S
  • 启动子线程

结论:

线程的运行是无序的,该结果也同样适用于进程.

3.2 主线程会等待子线程执行结束再结束

代码执行流程如下:

  • 导入线程模块
  • 创建死循环任务,延时0.3S
  • 创建子线程
  • 启动子线程,延时1S 结束后打印over

结论:

线程的运行中,主进程要等待子线程结束再结束.

解决办法:

  • 设置子线程守护主线程

sub_thread = threading.Thread(target=task,daemon=True)

 

sub_thread.setDaemon(True)

3.3 线程之间共享全局变量

需求: 创建两个任务,交给线程执行,一个任务全局变量执行添加数据,一个执行读取数据

代码执行流程如下:

  • 导入线程模块
  • 定义全局变量,创建两个任务,一个添加数据,一个执行读取数据
  • 创建子线程
  • 启动子线程,设置延迟观察效果

结论:

线程之间共享全局变量

原理: 不同线程之间的操作是在同一个进程内执行的.所以会共享全局变量

3.4 线程之间共享全局变量出现的错误问题

需求:创建两个任务,交给线程执行,分别执行从一百万个1相加,观察结果

代码执行流程如下:

  • 导入线程模块
  • 定义全局变量
  • 创建子线程
  • 启动子线程,观察结果

结论:

线程之间共享大量全局变量出现错误.

原理:

两个线程之间同步执行一个全局变量,出现重复错误

解决方法:

线程同步: 保证同一时刻只能有一个线程去操作全局变量 同步: 就是协同步调,按预定的先后次序进行运行。

  • join等待 一个线程全部执行后再执行
  • 互斥锁

四.互斥锁和死锁

4.1 互斥锁

1概念:对共享数据进行锁定,保证同一时刻只能有一个线程去操作.

注意点: 互斥锁是多个线程一起去抢,抢到的先执行,没抢到的只能等执行完毕,释放锁之后后再去抢.

2 使用方法:

锁是线程模块的一个变量,通过调用这个函数可以创建锁

# 创建锁 mutex = threading.Lock()

# 上锁 mutex.acquire() ...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定... # 释放锁 mutex.release()

3 注意点:

acquire(上锁)和release(释放)方法之间的代码同一时刻只能有一个线程去操作

如果在调用acquire(上锁)方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire(上锁)方法会堵塞,直到这个互斥锁释放后才能再次上锁。

acquire 和release 输出时候没有提示.手动输入

4 互斥锁完成2个线程对同一个全局变量各加100万次的代码操作

代码执行流程如下:

  • 导入线程模块
  • 定义全局变量
  • 创建锁

lock = threading.Lock()

  • 创建任务
  • 任务上锁,任务解锁

lock.acquire() lock.release()

  • 创建子线程
  • 启动子线程,观察结果

详解:

设置了互斥锁,哪个线程抢到数据我们不管,当一个线程抢到了数据,他就会执行完成,再释放.第二个任务再抢到去执行.这样我们就可以解决问题了

注意:

使用互斥锁,由多任务转变成了单任务执行,会降低执行效率

多个线程要同时使用互斥锁

互斥锁使用不好会变成死锁

4.2 死锁

1 概念: 一直等待对方释放锁的情景就是死锁

2 造成的后果:会造成应用程序的停止响应,不能再处理其它任务了。

3 死锁示例:

 

4 死锁产生,程序就会停止响应,应用程序就无法继续向下执行了

五.进程和线程的区别

1 关系对比:

  • 线程是依附在进程的,没有进程就没有线程
  • 一条进程默认一条线程,可以创建多条线程

2 区别对比

  • 进程之间不共享全局变量
  • 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步
  • 创建进程的资源开销要比创建线程的资源开销要大
  • 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
  • 线程不能够独立执行,必须依存在进程中
  • 多进程开发比单进程多线程开发稳定性要强

3 优缺点对比

  • 进程优缺点:
    • 优点:可以用多核
    • 缺点:资源开销大
  • 线程优缺点:
    • 优点:资源开销小
    • 缺点:不能使用多核

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值