多任务(进程线程)

我们先来了解一下学习知识点

  • 多任务:理解什么是多任务,为什么使用多任务
  • 进程:理解什么是进程,怎么使用进程完成多任务
  • 线程:理解什么是进程,怎么使用进程完成多任务
  • 注意事项:理解进程和线程的联系和区别,以及使用中的注意事项

一、多任务

我们之前写的代码,以函数为例, 都是先执行完一个函数之后,才能执行下一个函数,但实际情况是, 我们需要多个函数同时进行。同一时间段内同时执行多个任务(函数或方法),就是多任务

多任务有两种表现形式:并发和并行

现在我们的计算机CPU都是多核CPU,我们之前写的代码是从上往下单任务执行的,只能使用CPU中的一个核心,使用多任务可以充分利用CPU多个核心的计算能力,提高执行效率

并发:CPU核心数小于任务数,CPU轮流(交叉)执行多个任务

并行*CPU核心数大于等于任务数,每个CPU核心可以单独执行一个任务

二、进程

从代码开始运行起来我们就称之为进程,所有代码执行完毕,进程结束

代码执行需要向操作系统申请内存资源,而进程是操作系统资源分配的最小单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行

2.1多进程的使用:

程序运行起来, 在所有代码执行之前,会先默认创建一个主进程(即内存资源分配的过程),该进程会默认包含一个线程(即主线程),代码的执行实际上是由线程完成的。

使用多进程,其实就是由主进程,创建了多个子进程,每个子进程也都有对应要执行的任务

# 导入进程包
import multiprocessing
# 创建子进程对象并指定执行的任务
sub_process = multiprocessing.Process (target=任务名)
# 启动子进程执行任务
sub_process.start()
import multiprocessing
import time

# 任务1
def task1(count):
    for i in range(count):
        print("任务1执行中..")
        time.sleep(0.2)
# 任务2
def task2(num):
    for i in range(num):
        print("任务2执行中..")
        time.sleep(0.2)

if __name__ == '__main__':
    # group: 表示进程组,目前只能使用None
    # target: 表示执行的目标任务名(函数名、方法名)
    # name: 进程名称, 默认是Process-1, .....
    # args: 以元组的方式给任务传入参数
    # kwargs: 表示以字典方式传入参数
    sub_process1 = multiprocessing.Process(target=task1, args=(5,))
    sub_process2 = multiprocessing.Process(target=task2, kwargs={"num": 3})
    
    # 启动子进程执行对应的任务
    sub_process1.start()
    sub_process2.start()

2.2 获取进程编号

获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。

获取进程编号的两种操作

  • 获取当前进程编号:os.getpid()
  • 获取当前父进程编号 os.getppid()

2.3、进程的注意点

  1. 进程之间不共享全局变量
  2. 多进行的执行是无序的
  3. 主进程会等待所有的子进程执行结束再结束
    • 设置守护主进程方式: 子进程对象.daemon = True
    • 销毁子进程方式: 子进程对象.terminate()
    • 进程同步:子进程对象.join()

三、线程

线程是cpu调度的最小单位,是代码的真正执行者,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程

3.1多线程的使用:
import threading
import time

# 任务1
def task1(count):
    for i in range(count):
        print("任务1执行中..")
        time.sleep(0.2)
# 任务2
def task2(num):
    for i in range(num):
        print("任务2执行中..")
        time.sleep(0.2)

if __name__ == '__main__':
    # group: 表示线程组,目前只能使用None
    # target: 表示执行的目标任务名(函数名、方法名)
    # name: 线程名称, 默认是Process-1, .....
    # args: 以元组的方式给任务传入参数
    # kwargs: 表示以字典方式传入参数
    sub_thread1 = threading.Thread(target=task, args=(5,))
    sub_thread2 = threading.Thread(target=task, kwargs={"num": 3})
    
    # 启动子线程执行对应的任务
    sub_thread1.start()
    sub_thread2.start()

3.2、线程的注意点
  1. 线程之间执行是无序的

  2. 线程之间共享全局变量

  3. 线程之间共享全局变量数据容易出现错误问题

  4. 主线程会等待所有的子线程执行结束再结束

    • 设置守护主线程的方式:

      • sub_thread = threading.Thread(target=task, daemon=True)
        
      • sub_thread.setDaemon(True)
        
    • 线程同步:子线程对象.join()

3.3、互斥锁

在编程中,为了避免共享数据出现错误,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

互斥锁的使用
# 创建锁
mutex = threading.Lock()

# 上锁
mutex.acquire()

# 释放锁
mutex.release()

注意点:

  • acquire和release方法之间的代码同一时刻只能有一个线程去操作
  • 如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。
  • 互斥锁避免资源竞争
  • 互斥锁阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
  • 可能会造成死锁

四、进程和线程比较

1.进程是操作系统资源分配的最小单位,线程是CPU调度的最小单位
2.线程依附于进程,没有进程就没有线程,一个进程默认提供一个线程(主线程),进程可以创建多个线程
3.进程不共享全局变量,同一个进程中的线程共享全局变量(资源竞争–>互斥锁,互斥锁可能产生死锁)
4.进程开销大但可以利用多核(并行),线程开销小但不能利用多核(并发)

1.多任务

概念:在同一时间段内执行多个任务
好处呢就是 充分利用CPU,提高执行效率
表现形式:1.并发	CPU在一段时间内交替执行多个任务.
		2.并行	CPU在一段时间内只执行一个任务, 利用多个CPU完成执行多任务的功能

2.进程

概念:代码运行起来我们就称之为进程
	 是操作系统资源分配的最小单位
	 进程间不共享全局变量

使用方式:
1导包-import multiprocessing
2创建子进程:无参数 p = multiprocessing.Process(target=目标函数)
		  元组方式传参:p = multiprocessing.Process(target=目标函数, args=(1,))
		  字典方式传参:p = multiprocessing.Process(target=目标函数, kwargs={"num": 1})
3.启动子进程:p.start()

获取进程编号	获取当前进程编号:os.getpid()
			获取父进程编号:os.getppid()

主进程与子进程的执行顺序:
	正常情况下主进程会等待子进程执行结束后再结束
	设置守护主进程主进程代码执行完毕, 会关闭该子进程
	销毁子进程	子进程对象.terminate()
	等待子进程结束	子进程对象.join()
	多进程执行是无序的

3.线程

概念:线程是CPU调度的最小单位
	线程依附于进程,一个进程默认提供一个线程(主线程),进程可以创建多个线程
	一个进程中的所有线程共享全局变量

使用方式:
	导包	import threading
	创建子线程	无参数:t = threading.Thread(target=目标函数)
				元组方式传参:t = threading.Thread(target=目标函数, args=(1,))
				字典方式传参:t = threading.Thread(target=目标函数, kwargs={"num": 1})
启动子线程:t.start()

主线程与子线程的执行顺序:
	正常情况下主线程会等待子线程执行结束后再结束
	设置守护主线程主线程代码执行完毕, 会关闭该子线程	(t = threading.Thread(target=目标函数, daemon=True))	(线程对象.setDaemon(True))
	多线程执行是无序的

4.进程线程比较

1.进程是操作系统资源分配的最小单位,线程是CPU调度的最小单位
2.线程依附于进程,没有进程就没有线程,一个进程默认提供一个线程(主线程),进程可以创建多个线程
3.进程不共享全局变量,同一个进程中的线程共享全局变量(资源竞争-->互斥锁,互斥锁可能产生死锁)
4.进程开销大但可以利用多核(并行),线程开销小但不能利用多核(并发)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值