Python 的多进程与多线程编程(简单入门)

参考资料:https://www.bilibili.com/video/BV1fz4y1D7tU?p=1&vd_source=9c227976fc64451bd98c5af8637d55e0

1. 多任务的介绍

多任务:同一时间执行多个任务,现在的windows就是一个多任务系统,其优势在于充分利用资源提升执行效率。其主要包括:并行和并发两种方式。

  1. 并发:在一段时间内交替执行多个任务。(如单核的计算机)
  2. 并行:真正意义的同时执行。(如多核计算机同时运行)

2. 进程的介绍

进程:进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解: 一个正在运行的程序就是一个进程。

利用多进程执行多任务的举例:

# hello.py
def func_a():
	print('任务A')
def func_b():
	print('任务B')

func_a()
func_b()

在这里插入图片描述

3. 使用多进程完成多任务

本节讲解如何使用 python 库 multiprocessing 实现多进程编程。主要包括一下三个步骤:

  1. 步骤1: 引入 multiprocessing 多进程库文件
  2. 步骤2: 利用 multiprocessing.Process 多进程库创建多进程子对象,对象的初始化包括以下三个参数:
    target:参数为需要进行多进程处理的函数名;
    args:需要进行多进程处理的函数参数,以元组的形式;
    kwargs:需要进行多进程处理的函数参数,以字典的形式;
  3. 步骤3:利用类对象的 start 成员函数执行多进程
# 步骤1: 引入 multiprocessing 多进程库文件
import multiprocessing
import time
import os

def func_a(name):
	print('func_a 子进程编号: ', os.getpid())  # func_a 子进程编号:  33636
	print('func_a 的父进程编号: ', os.getppid()) # func_a 的父进程编号:  5268
    for _ in range(5):
        print('任务A..., {}'.format(name))
        time.sleep(1)
	
def func_b(name):
	print('func_b 子进程编号: ', os.getpid())  # func_b 子进程编号:  7004
	print('func_b 的父进程编号: ', os.getppid())  # func_b 的父进程编号:  5268
    for _ in range(5):
        print('任务B..., {}'.format(name))
        time.sleep(1)

if __name__ == '__main__':
	print('主进程编号: ', os.getpid())  # 主进程编号:  5268
	# 步骤2: 利用 multiprocessing.Process 多进程库创建多进程对象,需要指定 target 参数为函数名
	taskA = multiprocessing.Process(target=func_a, args=('Peter',))    # 使用 元组传参,顺序一致
	taskB = multiprocessing.Process(target=func_b, kwargs={'name': 'Jackey'})  # 字典传参,key名字一致
	
    # 步骤3:利用类对象的 start 成员函数执行多进程
	taskA.start()
	taskB.start()
     
'''
    输出内容:可以观察到 A,B 是交替同时执行的
        任务A..., Peter
        任务B..., Jackey
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
'''

4. 获取进程编号

进程编号的作用:当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程还有不同的子进程,那么就无法进行有效的进程管理,为了方便管理,实际上每个进程都是有自己编号的。可借助 os 库来查询进程或其父进程的编号。

import os
# 获取进程编号
os.getpid()
# 获取父进程的编号
os.getppid()

以上例代码为例,主进程是 main 函数,主进程创建了两个子进程,分别是 func_a 核 func_b,查询其父进程编号,与主进程一致。

5. 主进程与子进程的结束

当希望结束主进程时,默认情况下需要等待所有的子进程结束了才会结束主进程。当然,很多时候想要主进程结束后,所有子进程也同时自动销毁。这时需要设置守护主进程,达到该目的。具体可以通过以下实现:

子进程对象.daemon = True
import multiprocessing
import time

def subprocess():
    # 子进程工作 10 * 0.2 = 2s
    for i in range(10):
        print('子进程活跃中....')
        time.sleep(0.2)

if __name__ =="__main__":
    subprocess_ = multiprocessing.Process(target=subprocess)
    # 设置守护主进程
    subprocess_.daemon = True
    subprocess_.start()
    # 主进程只进行 1s, 那么主进程会比子进程先结束
    time.sleep(1)
    print("主进程结束啦!!!")

'''
1. subprocess_.daemon = False, 主进程会等待子进程结束
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....
主进程结束啦!!!
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....

2. subprocess_.daemon = True, 主进程结束,子进程自动销毁
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....
子进程活跃中....
主进程结束啦!!!
'''

6. 线程的介绍

首先回顾一下,进程的概念:进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解: 一个正在运行的程序就是一个进程。

那么进程和线程的关系是:线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序,同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

通俗来说,进程是一家公司,而线程是公司里面干活的打工人。所有的打工人共享公司的资源。

那么很容易解释为什么需要多线程,而不是多进程了。因为多进程相对于建立多家公司来分管某个业务,多家公司意味着可以使用更多的资源(多核并行),而多线程相当于一家公司雇佣众多廉价的打工人来分管业务。相比而言,多线程更加的节省资源(单核并发)

7. 多线程的使用方法

在 python 中多线程与多进程的使用方法极其相似,多线程的实现通过 threading 库实现。主要包括一下三个步骤:

  1. 步骤1: 引入 threading 多线程库文件
  2. 步骤2: 利用 threading.Thread 多线程库创建多线程子对象,对象的初始化包括以下三个参数:
    target:参数为需要进行多线程处理的函数名;
    args:需要进行多线程处理的函数参数,以元组的形式;
    kwargs:需要进行多线程处理的函数参数,以字典的形式;
    daemon:设置是否守护主线程,类似于第5节;
  3. 步骤3:利用类对象的 start 成员函数执行多线程
# 步骤1: 引入 threading 多线程库文件
import threading
import time
import os

def func_a(name):
    # print('func_a 子线程编号: ', os.getpid())
    # print('func_a 的父线程编号: ', os.getppid())
    for _ in range(5):
        print('任务A..., {}'.format(name))
        time.sleep(1)
	
def func_b(name):
    # print('func_b 子线程编号: ', os.getpid())
    # print('func_b 的父线程编号: ', os.getppid())
    for _ in range(5):
        print('任务B..., {}'.format(name))
        time.sleep(1)

if __name__ == '__main__':
    # print('主线程编号: ', os.getpid())
	# 步骤2: 利用 threading.Thread 多线程库创建多线程对象,需要指定 target 参数为函数名
    taskA = threading.Thread(target=func_a, args=('Peter',))    # 使用 元组传参,顺序一致
    taskB = threading.Thread(target=func_b, kwargs={'name': 'Jackey'})  # 字典传参,key名字一致
	
    # 步骤3:利用类对象的 start 成员函数执行多线程
    taskA.start()
    taskB.start()
     
'''
    输出内容:可以观察到 A,B 是交替同时执行的
        任务A..., Peter
        任务B..., Jackey
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
        任务B..., Jackey
        任务A..., Peter
'''

8. 线程的执行顺序

线程的执行顺序与创建顺序无关,其执行的顺序与CPU调度相关,通俗来说,其执行是无序的。可以通过以下例子证明:

import threading
import time

def task():
    time.sleep(1)
    current_thread = threading.current_thread()
    print(current_thread)

if __name__ == '__main__':
    # 创建 5 个子线程
    for i in range(5):
        subthread = threading.Thread(target=task)
        subthread.start()

'''
<Thread(Thread-3, started 25756)>
<Thread(Thread-2, started 14956)>
<Thread(Thread-4, started 38088)>
<Thread(Thread-5, started 35376)>
<Thread(Thread-1, started 38608)>
'''
  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值