• python - 进程、线程、协程


    系统知识

            计算机抽象组成:CPU + 存储器 + IO

            资源:

                    1.计算资源:CPU

                    2.存储资源:内存、磁盘等

    系统知识

            cpu时间片

            对于单核cpu同一时刻只能有一个任务运行

                    1.并发:交替执行(某时间段内的处理能力)

                    2.并行:同时执行

     进程与进程之间的通信方式

            管道

                    匿名管道(父子进程之间才能通信)

                    命名管道(不是父子进程也能通信)

            信号

                    异步通信

                    发送信号

                            硬件  ctrl + c

                            

            信号量

            共享内存

            socket

            消息队列

     进程线程

    ■ 系统知识
    • 线程:
    线程是操作系统最小的调度单位,
    是一串指令的集合
    • 进程:
    进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度
    的基本单位

    进程与线程

    • 真正在cpu上运行的是线程
    • 线程共享内存空间;进程的内存是独立的
    • 一个线程只能属于一个进程,而一个进程可以有多个线程, 但至少有一个线程
    • 资源分配给进程,同一进程的所有线程共享该进程的所有资 源。进程的资源是独立的
    • 同一个进程的线程之间可以直接交流;两个进程想通信,必 须通过一个中间代理来实现

     进程 VS 线程

    进程  vs  线程

    1.一个进程里面是可以有一个以上的线程,这些线程都是可以共享这个内存空间的

    2.不同进程之间内存空间都是独立的

    3.创建新的线程很简单,创建一个新的进程需要对其父进程进程一次克隆

    4.一个线程可以控制和操作同一个进程里的其他线程,进程只能操作子进程

    5.一个主线程改变,可能会影响 其他线程,改变父进程不会影响子进程

    多任务操作系统工作模式 

    多进程模式

    多线程模式

    多进程 + 多线程模式

    一般来说多线程的开销会比多进程少

    进程状态模型

    进程的三态模型

     进程的五态模型

     Linux五种状态:

            运行、中断,不可中断、僵尸、停止状态

    运行 ( 正在运行或在运行队列中等待 )               R
    中断 ( 休眠中, 受阻, 在等待某个条件的形成或接受到信号 )     S
    不可中断 ( 收到信号不唤醒和不可运行, 进程必须等待直到有中断发生 )     D
    僵尸 ( 进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放 )           Z
    停止 ( 进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行 )        T

     

    使用top命令查看这五种状态 

    load average :就绪 + 运行状态 线程队列的一个情况来反映出cpu一段时间内的繁忙程度

    例如:1核cpu load average = 1  表示已经处于满负荷的临界点了,load average >  1   就超负荷了。

               2核cpu load average = 2 表示已经处于满负荷的临界点,load average >2就超负荷了。

    load average: 0.03, 0.02, 0.05

    三个数值,代表1分钟,5分钟,15分钟,表示该时间段内的平均负载

    例如:15,5,3  表示最近的时间段负载增长过快

                 3,5,15表示最近的时间段负载降低

    系统监控命令的数据来源是 /proc目录

    /proc  目录在内存的,系统启动以后自动加载

    Threading - Thread

    threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。

    ■ Thread构造方法
    • 构造方法:
    Thread(group=None, target=None, name=None, args=(), kwargs={})
    • group: 线程组,目前还没有实现,库引用中提示必须是None;
    • target: 要执行的方法;
    • name: 线程名;
    • args/kwargs: 要传入方法的参数。
    ■ Thread实例方法
    • t.name:获取或设置线程的名称
    • t.getName()/setName(name): 获取/设置线程名。
    • t.is_alive()、t.isAlive():判断线程是否为激活状态。返回线程是否在运行。正在运行指启动后、终 止前。
    • t.ident :获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才 有效,否则它只返回None
    • t.run() :线程被cpu调度后自动执行线程对象的run方法
    • t.start(): 线程准备就绪,等待CPU调度,start会自动调用t.run()
    1. """
    2. @name : 01.threading模块
    3. @author : wangshenghu
    4. @projectname: tlbb
    5. @Date : 2022/8/5
    6. """
    7. import requests
    8. import functools
    9. import time
    10. import threading
    11. def runtime(func):
    12. # 保留传递进来的函数的元数据,将它的元数据赋值给inner
    13. @functools.wraps(func)
    14. def inner(*args, **kwargs): # 让装饰器更加通用
    15. start = time.time()
    16. result = func(*args, **kwargs)
    17. end = time.time()
    18. print(f"函数执行花了{end -start}s")
    19. return result
    20. return inner
    21. def get_content(url):
    22. text = requests.get(url).content
    23. time.sleep(0.5)
    24. print("get content")
    25. @runtime
    26. def main():
    27. t_list = []
    28. for i in range(5):
    29. # get_content("https://www.baidu.com")
    30. # 创建线程
    31. # target -->指定传入的方法的名字 , 要做什么
    32. # args --》 指定方法需要传入的参数 元组类型
    33. # 子线程只做target里面规定的事情
    34. t = threading.Thread(target=get_content, args=("https://www.baidu.com",))
    35. t_list.append(t)
    36. t.start() # 启动线程 #--》自动执行run方法
    37. for t in t_list:
    38. # 阻塞当前环境上下文,知道t的线程执行完成
    39. # 谁执行这个join代码,谁就是当前环境
    40. t.join()
    41. main()

    执行结果:

    1. E:\python\python3.9.1\python.exe E:/tlbb/2022-08-05-多进程-多线程-协程/01.threading模块.py
    2. get contentget content
    3. get content
    4. get content
    5. get content
    6. 函数执行花了0.8148140907287598s
    7. Process finished with exit code 0
    • t.join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout 可选参数)。
    • t.setDaemon(bool): 设置是后台线程(默认前台线程(False))。(在start之前设置)
    • 如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止
    • 如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程 也执行完成后,程序停止
    • t.isDaemon:判断是否为后台线程

    Threading - Lock

    在多线程中使用lock可以让多个线程在共享资源的时候遵循一定的规则。
    常见锁类型
    • Lock()/RLock:普通锁(互斥锁)
    解决资源争用,数据读取不一致等
    • Semaphore :信号量
    最多允许同时N个线程执行内容
    • Event: 事件锁
    根据状态位,决定是否通过事件
    • Condition: 条件锁

    示例:互斥锁

    没有添加互斥锁的时候,使用多线程可能会出现资源占用的情况

    1. import threading
    2. import time
    3. num = 0
    4. def sum_num(i):
    5. global num
    6. time.sleep(0.5)
    7. num += i
    8. print(num)
    9. for i in range(5):
    10. t = threading.Thread(target=sum_num, args=(i,))
    11. t.start()

    执行结果:

    1. E:\python\python3.9.1\python.exe E:/tlbb/2022-08-05-多进程-多线程-协程/03.互斥锁.py
    2. 47
    3. 7
    4. 910

     示例:添加互斥锁以后

    1. """
    2. @name : 03.互斥锁
    3. @author : wangshenghu
    4. @projectname: tlbb
    5. @Date : 2022/8/5
    6. """
    7. # 互斥锁,
    8. import threading
    9. import time
    10. num = 0
    11. def sum_num(i):
    12. # 获取锁,同一时间只能有一个线程在这里运行,解决资源占用问题
    13. lock.acquire()
    14. global num
    15. time.sleep(0.5)
    16. num += i
    17. print(num)
    18. # 释放锁
    19. lock.release()
    20. # 创建一个锁对象
    21. lock = threading.Lock()
    22. for i in range(5):
    23. t = threading.Thread(target=sum_num, args=(i,))
    24. t.start()

     执行结果

    1. E:\python\python3.9.1\python.exe E:/tlbb/2022-08-05-多进程-多线程-协程/03.互斥锁.py
    2. 0
    3. 1
    4. 3
    5. 6
    6. 10

    还可以使用with语句来写互斥锁语句

    1. with lock:
    2. global num
    3. time.sleep(0.5)
    4. num += i
    5. print(num)

    进程与多进程

    什么是进程?

    进程(Process)是计算机中的程序关于某数据 集合上的一次运行活动,是系统进行资源分配的基本单位。

    进程的创建

    创建:用户创建出来的所有进程都是由操作系统负责,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的

     如何创建子进程

    在python中,每一个运行的程序都有一个主进程,可以利用模块中封装的方法来创建子进程

     os.fork:创建子进程

    os.fork中就用来创建子进程的方法
    注意:这个os.fork()方法只有在unix系统中才会有,在window下没有。
    • 使用fork创建子进程后,操作系统会将当前的进程复制一份
    • 原来的进程称为父进程,新创建的进程称为子进程
    • 两个进程会各自互不干扰的执行下面的程序
    • 父进程与子进程的执行顺序与系统调度有关
    • 在子进程内,这个方法会返回0;在父进程内,这个方法会返回子进程的编号PID

     n os.fork:创建子进程

    • os.fork的返回值:
    • 返回值为大于0时,此进程为父进程,且返回的数字为子进程的PID;
    • 当返回值为0时,此进程为子进程。
    • 如果返回值为负数则表明创建子进程失败。
    • 父进程结束时,子进程并不会随父进程立刻结束。同样,父进程不会等待子进程执行完。
    n os.getpid()
    :获取进程的进程号。
    n os.getppid():获取父进程的进程号。

     示例:在linux系统里面使用功能os.fork()创建子进程

    1. import os,time
    2. print("start")
    3. result = os.fork()
    4. print("outside pid is", result)
    5. if result == 0:
    6. print("child process")
    7. time.sleep(60)
    8. print("child pid is:", os.getpid())
    9. print("child-parent pid is :", os.getppid())
    10. else:
    11. print("parent process")
    12. time.sleep(60)
    13. print("parent pid is: ", os.getpid())

     使用python3运行py程序后,查看进程

     运行结果:

    1. [root@wangsh 8yue10]# python3 os_fork.py
    2. start
    3. outside pid is 6609
    4. parent process
    5. outside pid is 0
    6. child process
    7. parent pid is: 6608
    8. child pid is: 6609
    9. child-parent pid is : 6608

     

  • 相关阅读:
    【从零开始学习 SystemVerilog】2.10、SystemVerilog 数据类型—— Associative Array(关联数组)
    小程序常用组件小结
    [Rust学习:四] Vec和栈
    ChatGPT Plus 支付出现「您的银行卡被拒绝/your card has been declined」怎么办?
    C++ 【2】
    在android中使用java反射机制的利弊分别是那些?与导入包名类名,androidmk追加对应jar包相比,二者差异是什么?
    qiankun - 微前端应用搭建
    Redis从理论到实战:如何使用redis实现短信登录与注册?
    element-plus-自定义Dialog样式
    0729放假自习
  • 原文地址:https://blog.csdn.net/qq_48391148/article/details/126172298