Python概念之多线程+多进程+协程

Python概念之多线程+多进程+协程

文案来自于

多进程

多线程

协程

只是将文案中的代码自己修改了一下,解决了自己对于文章中不懂的地方,谨做记录,平时工作中,除了appium处理多个手机的时候用过多进程,其实很少用到这方面的知识,所以做一个简单的梳理,概念性的记录

多进程

linux或者Mac操作系统

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

import os

print(os.getpid())
pid = os.fork()  # 什么都不会打印,只是会创建一个子进程,从此之后执行的所有操作,都会父进程执行一遍,子进程执行一遍
print(pid)  # 但是子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。
print("hello")
print("erzi")
print(os.getppid())  # 打印父进程

结果

19499 #这个就是print(os.getpid())打印出来的当前的进程id
19500 #这个就是父进程执行print(pid)打印出来的,父进程返回子进程的ID
hello #这个就是父进程执行print("hello")打印出来的
erzi #这个就是父进程执行print("erzi")打印出来的
50863 #这个就是父进程执行print(os.getppid()),获取了父进程的id
0 #这个就是子进程执行print(pid)打印出来的,但是子进程永远返回0
hello #这个就是子进程执行print("hello")打印出来的
erzi #这个就是子进程执行print("erzi")打印出来的
19499 #这个就是子进程执行print(os.getppid()),获取了父进程的id,也就是第一行打印出来的19499

综上所述,新建了一个进程之后,父进程做啥,子进程就做啥

多平台multiprocessing

由于Windows没有fork调用,上面的代码在Windows上无法运行。而Mac系统是基于BSD(Unix的一种)内核,所以,在Mac下运行是没有问题的,推荐大家用Mac学Python!

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象

import multiprocessing


def add(name):
    print(name + name + name)


if __name__ == '__main__':
    for i in range(3):
        p = multiprocessing.Process(target=add, args=(i,))  # target就是目标执行函数,args里就是函数的入参
        p.start()
        p.join()  # join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
        print('进程%d执行完毕' % i)

结果

0
进程0执行完毕
3
进程1执行完毕
6
进程2执行完毕

如果我们注释掉p.join()这一行,结果就成了下面这样,所以join更通俗易懂的解释应该是,有了join,就可以让一个进程执行完毕之后再执行另一个进程

进程0执行完毕
进程1执行完毕
进程2执行完毕
0
3
6

多线程

Python的标准库提供了两个模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行

import threading


def add(name):
    print('现在运行的线程为%s' % threading.current_thread().name)
    print('name的值为%s' % name)


if __name__ == '__main__':
    for i in range(3):
        p = threading.Thread(target=add, args=(i,))
        p.start()
        p.join()
        print('第%d个线程执行完毕' % i)

结果

现在运行的线程为Thread-1
name的值为0
第0个线程执行完毕
现在运行的线程为Thread-2
name的值为1
第1个线程执行完毕
现在运行的线程为Thread-3
name的值为2
第2个线程执行完毕

我们再把p.join()注释掉,执行结果是乱的,每一次都不一样

现在运行的线程为Thread-1
name的值为0
第0个线程执行完毕
现在运行的线程为Thread-2
name的值为1
第1个线程执行完毕
现在运行的线程为Thread-3
name的值为2
第2个线程执行完毕

Lock

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

协程

协程 ,又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元。因为它自带CPU的上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程。

通俗的理解: 在一个线程中的某个函数中,我们可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的 ,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。

协程与线程的差异:
在实现多任务时, 线程切换__从系统层面__远不止保存和恢复CPU上下文这么简单。操作系统为了程序运行的高效性,每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作,所以线程的切换非常耗性能。但是__协程的切换只是单纯地操作CPU的上下文__,所以一秒钟切换个上百万次系统都抗的住。

import time


def one():
    while True:
        print("one start")
        yield  #带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的
        print("one end")
        time.sleep(0.5)


def two():
    while True:
        print("two start")
        yield
        print("two end")
        time.sleep(0.5)


if __name__ == "__main__":
    t1 = one()  # 生成器对象
    t2 = two()
    while True:
        next(t1)  # 1、唤醒生成器t1,执行到yield后,保存上下文,挂起任务;下次再次唤醒之后,从yield后继续往下执行,调用next()函数获取由yield语句返回的下一个值
        print("\nThe main thread!\n")  # 2、继续往下执行
        next(t2)  # 3、唤醒生成器t2,....

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值