Python并发编程-进程

这篇博客详细介绍了Python中的进程编程,包括进程理论、进程的使用、join方法、进程调度、僵尸进程与孤儿进程、进程对象及其方法、守护进程、互斥锁、队列、IPC机制以及生产者消费者模型。重点讲解了进程的三种状态、同步异步概念、进程间通信以及进程池的应用。
摘要由CSDN通过智能技术生成

一:进程理论

1.程序和进程

程序就是一堆代码文件,是指令和数据的集合,可以作为目标文件保存在磁盘中,或者作为段存放在内存地址空间中。(静态)

进程就是一个程序运行的过程,是操作系统进行资源分配和保护的基本单位(动态)

进程资源分配的最小单位

1个程序可以对应多个进程,但是1个进程只能对应1个程序。进程和程序的关系犹如演出和剧本的关系

2.并发、并行、串行

**串行:**多个任务依次运行、一个进程运行完毕、在运行下一个

**并发:**多个任务看起来像是在同时运行,本质上还是一个个地运行

**并行:**多个任务真正意义上一起运行,只有多个CPU才能并行的概念

img

3.同步-异步/阻塞 - 非阻塞

同步(慢)
发起一个请求,直到请求返回结果之后,才进行下一步操作

​ 就行运行在一段代码,自上而下一行一行运行,只有上一行代码执行完成,才能执行下一行代码

​ 简单来说:同步就是:必须一件事一件事的做等前一件事做完了,才能做下一件事

异步(快):

发起一个请求后,不管这个请求有没有返回结果,直接可以进行下一步操作

​ 一般情况下,有一个回调的操作

​ 简单来说:异步就是:可以多件事情同时做

阻塞(慢):

​ 遇到IO操作,CPU会被操作系统切换到其它进程。

调用结果返回之前,当前线程会被挂起。调用线程只用在得到结果之后才会返回。

​ 简单来说,阻塞就是:需要买奶茶和冰淇淋,买奶茶的时候奶茶制作过程只能等,然后买完奶茶再去买冰淇淋

非阻塞(快):

​ 没有IO操作,一直运行

在不能立刻得到结果之前,改调用不会阻塞当前线程

​ 简单来说,非阻塞就是:需要买奶茶和冰淇淋,买奶茶的时候等的时间去买冰淇淋。

最佳状态:异步+非阻塞
拓展:

​ 同步框架:FlakDjango3.0之前

​ 异步框架:TornadoSanicFastAPI

例子:

背景:妈妈出门了,小明想要看电视,但是妈妈让小明烧水。

1.小明烧水,在一旁等待,时不时看一下水有没有烧开。 —— 同步阻塞

2.小明烧水,等待的时间偷偷去看会儿电视,看电视的时候时不时来看一下水有没有烧开。 —— 同步非阻塞

3.小明买了一个水开了之后会响的水壶来烧水,烧水时在一旁等着,不用时不时看水有木有烧开。 —— 异步阻塞

4.小明用那个会响的水壶烧水,然后去看电视,等水开了之后发出响声,再去开。 —— 异步非阻塞

4.进程运行的三种状态

打开一个应用程序,该进程会进入就绪态,获取CPU资源后,执行程序代码,进入运行态,出现了读写文件with open(…),就会进入阻塞态,CPU资源就会被操作系统分配到其他进程

运行态(Running)(非阻塞):进程已获CPU,正在执行。单处理机系统中。处于执行状态的进程只有一个;多处理机系统中,有多个处于执行状态的进程。

就绪态(Ready)(非阻塞):进程以获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列

阻塞态(Blocked)(阻塞):正在执行的进程由于某种原因(**文件读写等IO操作)而暂时**无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)

就绪 → 运行

处于就绪状态的进程,当进程调度程序为之分配了处理器后,该进程便由就绪状态转变成运行状态。

运行 → 就绪

处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从运行状态转变成就绪状态。

运行 → 阻塞

正在执行的进程因等待某种事件(文件读写等IO操作)发生而无法继续执行时,便从运行状态变成阻塞状态。

阻塞 → 就绪

处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。

img

优化程序效率的核心法则

降低IO操作(硬盘IO、网络IO)

数据获取优先级:

内存 > 本地硬盘 > 网络IO

5.提交任务的2种方式

同步调用:一个任务必须要执行完毕才能执行另一个任务

异步调用:一个任务在执行过程中,可以执行另一个任务

二:进程的使用

### 1.进程的创建

Windows:CreateProcess

Linux:Fork

2.进程的终止

1.正常退出(自愿;程序执行完毕后,终止,资源被回收)

2.出错退出(自愿;python3 test.py但是test.py不存在)

3.严重错误(非自愿;执行非法指令)

4.被其他进程杀死(非自愿;被操作系统杀死taskkill /F /PID 3333

三:join方法

0.作死法

import time
from multiprocessing import Process


def task(n):
    print('%s run' % n)
    time.sleep(10000)


if __name__ == '__main__':
    for i in range(10000):
        p = Process(target=task, args=(1,))
        p.start()
    print('主进程')

1.单个进程 + 单个子进程

import os
import time
from multiprocessing import Process


def task(n):
    print(f'task[{os.getpid()}] is running')
    time.sleep(n)
    print(f'task[{os.getpid()}] is done')


if __name__ == '__main__':
    # p = Process(target=task, args=(5,))       # args中为元组/列表
    p = Process(target=task, kwargs={
   'n': 5})   # kwargs中为字典
    p.start()  # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
    print(f'主进程[{os.getpid()}]')

# 主进程[15188]
# task[86956] is running
# task[86956] is done

2.单个父进程 + 多个子进程 方式1

import os
import time
from multiprocessing import Process


def task(n, name):
    print(f'task[{name}] is running [{os.getpid()}]')
    time.sleep(n)
    print(f'task[{name}] is done [{os.getpid()}]')


if __name__ == '__main__':
    p1 = Process(target=task, args=(2, 'p1'))
    p2 = Process(target=task, args=(2, 'p2'))
    p3 = Process(target=task, args=(2, 'p3'))

    p1.start()  # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
    p2.start()  # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
    p3.start()  # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢

    print(f'主进程[{os.getpid()}]')


# 主进程[79800]
# task[p1] is running [17320]
# task[p3] is running [66276]
# task[p2] is running [36784]
# task[p1] is done [17320]
# task[p3] is done [66276]
# task[p2] is done [36784]

3.单个父进程 + 多个子进程 方式2

import os
import time
from multiprocessing import Process


class Myprocess(Process):
    def __init__(self, tag):
        super().__init__()
        self.tag = tag

    def run(self) -> None:    # 表示run函数的返回值为空,无返回值
        print(f'task[{self.tag}] is running [{os.getpid()}]')
        time.sleep(3)
        print(f'task[{self.tag}] is done [{os.getpid()}]')


if __name__ == '__main__':
    p1 = Myprocess('进程1')
    p2 = Myprocess('进程2')
    p3 = Myprocess('进程3')

    p1.start()  # 相当于:p1.run,但是p1.run是同步的,所以这里用p1.start
    p2.start()  # p2.run
    p3.start()  # p3.run

    print(f'主进程[{os.getpid()}]')

4.进程之间的数据是隔离的

​ 因为:开启1个进程,就是开启了1个Python解释器。

Windows下要开进程,需要在main函数

import os
import time
from multiprocessing import Process

age = 18

def task(n, name):
    global age  # 局部修改全局
    age = 99
    print(f'task[{name}] is running [{os.getpid()}]')
    time.sleep(n)
    print(f'task[{name}] is done [{os.getpid()}]')
    print(f'子进程的age:{age}')


if __name__ == '__main__':
    p1 = Process(target=task, args=(1, 'p1'))
    p1.start()
    print(f'主进程[{os.getpid()}]')
    print(f'主进程的age:{age}')

# 主进程[2680]
# 主进程的age:18
# task[p1] is running [45116]
# task[p1] is done [45116]
# 子进程的age:99

5.join - 让子进程的开启者 等p1开启并运行完毕后 再运行

import os
import time
from multiprocessing import Process


def task(n, name):
    print(f'task[{name}] is running [{os.getpid()}]')
    time.sleep(n)
    
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值