一文看懂Python多进程与多线程

一文看懂Python进程与线程

多任务:就是指在同一时间内执行多个任务,现在的电脑安装的操作系统也都是多任务系统,比如你可以同时打开QQ、微信、音乐等应用程序。

多任务分为两种执行方式

  • 并发
  • 并行

并发:就是指在一段时间内交替的去执行任务
原理是这样的,对于单核的cpu处理多任务的时候,操作系统是轮着让各个软件执行的,比如你目前开着QQ、微信、网易云音乐,我们使用起来看似是同时执行,其实操作系统是让QQ执行0.03秒,然后让微信执行0.03秒,再让网易云执行0.03秒。他们是交替的执行的,但是因为cpu处理的速度太快,所以我们根本感觉不到。

并行:是真正意义上的多个任务一起执行
原理就是对于多核的cpu处理多任务的时候,操作系统是给每一个正在运行的软件分配到一个cpu内核中,然后通过多个cpu内核一起执行多个软件。从而达到同一个时间点上真正有多个软件在同时运行。

那么为什么要使用多任务呢?

  1. 因为如果没有多任务,也就代表这我们的cpu在同一时刻只能处理一个软件,比如你打开了QQ,你就只能进行QQ聊天。不能上微信,也不能玩游戏。大大降低了用户的体验,也大大浪费了cpu的性能资源
  2. 使用了多任务就可以充分利用我们cpu的资源,并且提高程序的执行效率。同时也大大提高用户的体验。

接下来我们再说怎样能够实现多任务。

在我们的python中,实现多任务有两种方式:进程和线程

进程:比如你的计算机现在打开了网易云,那么这个网易云就算是一个进程,它正在占用cpu的资源,通俗的说一个正在运行的程序或者软件就是一个进程,这个进程就是操作系统进行资源分配的基本单位。

线程:是依附在进程中的,没有进程也就没有线程,它就是进程中的一个分支,是cpu调度的基本单位。每个进程至少有一个线程(主线程)。同时线程也是实现多任务的一种方式。

Python中的多进程:

Process进程类的说明

Process([group [, target [, name [, args [, kwargs]]]]])
• group:指定进程组,目前只能使用None
• target:执行的目标任务名(方法或函数名)
• name:当前进程名字,默认为Process-N
• args:以元组方式给执行任务传参
• kwargs:以字典方式给执行任务传参

Process创建的实例对象的常用方法:
• start():启动子进程实例(创建子进程)
• join():等待子进程执行结束
• terminate():不管任务是否完成,立即终止子进程

Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

接下来开始动手操作啦。

首先 不使用 进程来进行一下实验结果:

import time


#任务一:QQ
def QQ(name_1):
    for i in range(3):
        print("正在使用QQ和%s聊天中..." % name_1)
        time.sleep(1)


#任务二:微信
def Wechat(name_2):
    for i in range(3):
        print("正在使用微信和%s视频中..." % name_2)
        time.sleep(1)


if __name__ == '__main__':

    #直接调用两个函数
    QQ("涛涛")

	Wechat("狗楠")

运行结果:

通过结果可以看出来由于先调用的QQ任务的函数,所以先完成了第一个for循环中的输出,待循环结束后,才开始调用第二个Wechat任务的函数,进行输出结果。可以看出来是单任务执行模式。

接下来使用 进程来对比一下实验结果:

#导入进程模块
import multiprocessing
#为了验证两个进程是同时进行的,导入time模块
import time


#任务一:QQ
def QQ(name_1):
    for i in range(3):
        print("正在使用QQ和%s聊天中..." % name_1)
        time.sleep(1)


#任务二:微信
def Wechat(name_2):
    for i in range(3):
        print("正在使用微信和%s视频中..." % name_2)
        time.sleep(1)

if __name__ == '__main__':
    # 创建QQ和微信的子进程
    # group: 表示进程组,目前只能使用None
    # target: 表示执行的目标任务名(函数名、方法名)
    # name: 进程名称, 默认是Process-1, .....一般不进行设置
    # 这里进行了传参,args是位置参数,kwargs是关键字参数

    # 创建QQ进程对象
    QQ_process = multiprocessing.Process(target=QQ, args=("涛涛",))
    # 创建Wechat进程对象
    Wechat_process = multiprocessing.Process(target=Wechat,kwargs={"name_2":"狗楠"})

    # 启动子进程执行对应的任务
    QQ_process.start()
    Wechat_process.start()

在这里插入图片描述
通过结果可以看出来,QQ和微信两个函数进程是同时运行的,在同一时刻会出现两条显示结果,所以通过python的进程模块实现了多任务。(使用pycharm进行测试可以更好的体验多任务)

进程相关操作:
获取当前进程编号和父类进程编号的方法:(目的是查看子进程和父进程之间的关系)

  1. 导入os模块
  2. 当前进程:在需要获取进程编号的进程中输入os.getpid()
  3. 父进程:输入os.getppid()
    get获取,当前进程中的p代表process进程,id即编号,父类进程编号中的第一个p是parent。

获取当前进程的方法:
multiprocessing.current_process()
获取当前进程的名字:
multiprocessing.current_process().name

注意:
(1)进程执行任务并传参有两种方式:

• 元组方式传参(args): 元组方式传参一定要和参数的顺序保持一致。
• 字典方式传参(kwargs): 字典方式传参字典中的key一定要和参数名保持一致。

(2)进程之间是不共享全局变量

(3)主进程会等待所有子进程运行结束后再结束
如果想要实现主进程退出了子进程就不再执行,可以使用 守护主进程 或者 销毁子线程来解决。

  1. 将子进程的属性dearmon设置为True
    multiprocessing.Process(target=show_info, daemon=True)或
    子进程对象.daemon = True
  2. 在主进程退出之前将子进程销毁
    子进程对象.terminate()

Python中的多线程

线程类Thread参数说明

Thread([group [, target [, name [, args [, kwargs]]]]])
• group: 线程组,目前只能使用None
• target: 执行的目标任务名
• args: 以元组的方式给执行任务传参
• kwargs: 以字典方式给执行任务传参
• name: 线程名,一般不用设置
Thread创建的实例对象的常用方法:
• start():启动子线程实例(创建子线程)
• join():等待子线程执行结束
Thread创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

不使用线程的实验结果和不使用进程的实验结果一致,这里就不再重复。 直接使用多线程来进行实验:

使用线程代码:

#导入线程模块
import threading
#为了验证两个线程是同时进行的,导入time模块
import time


#任务一:QQ
def QQ(name_1):
    for i in range(3):
        print("正在使用QQ和%s聊天中..." % name_1)

        time.sleep(1)


#任务二:微信
def Wechat(name_2):
    for i in range(3):
        print("正在使用Wechat和%s视频中..." % name_2)
        time.sleep(1)


if __name__ == '__main__':
    # 创建QQ和微信的子线程
    # group: 表示线程组,目前只能使用None
    # target: 表示执行的目标任务名(函数名、方法名)
    # name: 线程名称, 默认是Process-1, .....一般不进行设置
    # 这里进行了传参,args是位置参数,kwargs是关键字参数

    # 创建QQ线程对象
    QQ_process = threading.Thread(target=QQ, args=("以前的人",))
    # 创建Wechat线程对象
    Wechat_process = threading.Thread(target=Wechat, kwargs={"name_2": "酷的人"})

    # 启动子线程执行对应的任务
    QQ_process.start()
    Wechat_process.start()

在这里插入图片描述

通过结果可以看出来,QQ和微信两个函数线程是同时运行的,在同一时刻会出现两条显示结果,所以通过python的线程模块实现了多任务。(使用pycharm进行测试可以更好的体验多任务)

线程相关操作:

获取当前线程编号和父类线程编号的方法:(目的是查看子线程和父线程之间的关系)

  1. 导入os模块
  2. 当前线程:在需要获取线程编号的进程中输入os.getpid()
  3. 父线程:输入os.getppid()

获取当前进程的方法:
threading.current_thread()
获取当前进程的名字:
threading.current_thread().name

注意:
(1)线程执行任务并传参有两种方式:
• 元组方式传参(args) :元组方式传参一定要和参数的顺序保持一致。
• 字典方式传参(kwargs):字典方式传参字典中的key一定要和参数名保持一致。
(2)线程之间共享全局变量,但是会产生全局变量数据错误的问题

数据错误原因:首先因为线程是共享全局变量的,然后在程序执行时多个线程同时运行,他们之间拿到全局变量没有任何限制,线程1想拿全局变量就拿,想要更改数据,这时线程2又把全局变量抢过去更改数据,更改之后,线程1又会更改数据,而此时线程1并不知道新的全局变量值,所以是按着自己的节奏给全局变量赋值。所以导致数据出现错误。
解决方法:1.线程等待(join) 2.互斥锁

1. 使用线程等待方法解决线程数据错误问题

import threading
    #定义全局变量
    num = 0
    #循环一次给全局变量加1
    def sum_num_1():
        for i in range(100000):
            global num
            num += 1
        print("sum1:", num)
    
#循环一次给全局变量加1
def sum_num_2():

    for i in range(100000):
        global num
        num += 1
    print("sum2:", num)
if __name__ == '__main__':
    # 创建两个线程
    first_thread = threading.Thread(target=sum_num_1)
    second_thread = threading.Thread(target=sum_num_2)
    # 启动线程
    first_thread.start()
    # 线程等待
    first_thread.join()
    # 启动线程
    second_thread.start()

在这里插入图片描述

2. 使用互斥锁解决线程数据错误问题

import threading

#定义全局变量
num = 0

#循环一次给全局变量加1
def sum_num_1():
    #上锁
    lock.acquire()
    for i in range(100000):
        global num
        num += 1
    print("sum1:", num)
    #解锁
    lock.release()

#循环一次给全局变量加1
def sum_num_2():
    #上锁
    lock.acquire()
    for i in range(100000):
        global num
        num += 1
    print("sum2:", num)
    #解锁
    lock.release()

if __name__ == '__main__':
    # 创建一个锁
    lock = threading.Lock()
    # 创建两个线程
    first_thread = threading.Thread(target=sum_num_1)
    second_thread = threading.Thread(target=sum_num_2)
    # 启动线程
    first_thread.start()
    # 启动线程
    second_thread.start()

在这里插入图片描述

其实线程等待和互斥锁的初衷都是一样的,保证其中一个线程能够完完整整的执行完代码之后再执行另一个线程。但会降低程序的执行效率,使多任务变成了单任务。

(3)主线程会等待所有子线程运行结束后再结束

如果想要实现主线程退出了子线程就不再执行,可以使用 守护主线程方式来解决
设置守护主线程有两种方式:
1.threading.Thread(target=show_info, daemon=True)
2.子线程对象.setDaemon(True)

最后来总结一下进程和线程:

进程和线程对比的三个方向
一、 关系对比
二、 区别对比
三、优缺点对比

  1. 关系对比:
    (1) 线程是依附在进程中的,没有进程就没有线程
    (2) 一个进程默认有一个线程,进程也可以创建多个线程
    (3) 都是实现多任务的一种方式
  2. 区别对比:
    (1) 进程之间不共享全局变量
    (2) 线程之间共享全局变量,但是要注意资源竞争问题,解决方法:互斥锁和线程等待
    (3) 创建进程的资源开销要比创建线程的资源开销要大
    (4) 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
    (5) 线程不能独立运行,必须依附在进程中
    (6) 多进程开发比单进程多线程的开发稳定性要强
  3. 优缺点对比:
    (1) 进程优缺点:
    优点:可以使用多核
    缺点:资源开销大
    (2) 线程优缺点:
    优点:资源开销小
    缺点:不能使用多核
  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值