Python - 进程、线程与协程
在操作系统中,每一个独立运行的程序,都占有 操作系统 分配的资源,这些程序中间互不干涉,都只负责运行自己的程序代码,这就是进程。
但是当操作系统频繁的创建销毁进程时,大量的系统资源被浪费在创建和销毁的过程中。而随着多核心 cpu 的出现,线程也逐渐代替了进程,成为了操作系统 可以独立运行的基本单位。
当进程不是多线程程序时,存在于进程当中的唯一线程,便是进程本身运行的代码块。
而当多线程出现在一个进程中时,则多个线程之间共享此进程的资源,并接受操作系统的调度来运行每个线程。
状态
协程
为了了解协程的概念,我们先来了解一下并发和并行。
并行
并行比较好理解,即有多个程序在同时执行,这里的程序指的是操作系统的线程。
每个 cpu 核心,只能在同一个时刻运行一组指令,意味着同一时刻,一个核心上只有一个线程在执行。
当 cpu 有四个核心时,他只可以 执行4个线程。
并发
想要了解并发,就需要知道 和 。
同步阻塞
当程序中的一个 I/O 操作,会占据比较长的时间,这时候,程序没有被挂起,且一直在等待网络数据传输,无法进行其他操作,这时候就是同步阻塞。
同步的一个概念就是,网络传输完成后也无法告知主程序操作完成,这就导致了主程序:
- 要么只能进行等待 I/O 完成
- 要么轮询去查看是否传输是否已经完成
当然,轮询时候可以进行其他的操作,这时候,就是非阻塞的状态,即 同步非阻塞。
同步非阻塞
非阻塞的概念即主程序可以进行其他的操作。
异步阻塞
有同步,就有异步。
而异步阻塞与同步阻塞相同,主程序啥也不干,就等着 I/O 操作完成。
异步非阻塞
异步非阻塞状态,便是并发的关键。
当主程序使用异步 I/O 操作时,并不会影响主程序后续的运行,而当异步 I/O 操作完成后,会主动通知主程序进行其他操作,这样就减少了轮询过程中的资源消耗,专注于其他工作。
并发
而并发就是 异步非阻塞 状态下的一种形式,当程序执行操作 a 时,使 a 的 I/O 异步操作,这时程序去执行操作 b, 在外部看来,a 和 b 时同时被执行的,然而他们只运行在在一个线程当中。
与线程、进程不同的是,协程并不是操作系统物理层面存在的一种程序。
协程是程序级别的,由程序编写者自己操控整个协程的生命周期。这样就实现了类似操作系统操作多线程一样的效果,但是省下了现成的切换中造成的资源消耗。
而通过程序来操纵协程,就造成了cpu 一直在运行,并且是多个协程一直在运行的假象,也就变成了并发。
实例
下面我们通过几个实例来说明,在 python 中的进程、线程和协程的关系。
进程实例
在 python 中我们如何编写多进程的程序呢?
答案是使用模块 multiprocessing 进行实现。
import timefrom multiprocessing import Processclass Test(Process): def __init__(self): super().__init__() def run(self): while True: print('process b is run') time.sleep(1)
通过继承 multiprocessing 的 Process ,实现进程类,然后实现 run 方法,即可在此方法中实现进程要运行的内容。
from process_b import Testimport timeif __name__ == '__main__': t = Test() t.start() while True: print('process a run') time.sleep(1)
调用方法也非常简单,直接使用 Test 实例化对象,然后调用对象的成员函数 start() 即可。
python3 process_a.pyprocess a runprocess b is runprocess b is runprocess a runprocess a runprocess b is runprocess b is runprocess a run
线程实例
Cpython 中由于存在 GIL ,所以多线程在实际应用中也会变为单核 cpu 上的线程,排队运行。
import threadingimport timeclass ThreadTest (threading.Thread): def __init__(self, name): super().__init__() self.name = name def run(self): while True: print(f'i am in thread {self.name}') time.sleep(1)if __name__ == '__main__': threads = [] for i in range(4): t = ThreadTest(i) threads.append(t) for t in threads: t.start() for t in threads: t.join()
通过继承 threading.Thread 来实现线程类,然后通过实例化,生成对象,调用对象的 start() 即可启动线程。
运行结果
python3 thread_a.pyi am in thread 0i am in thread 1i am in thread 2i am in thread 3i am in thread 1i am in thread 3i am in thread 0i am in thread 2i am in thread 1i am in thread 3i am in thread 0i am in thread 2i am in thread 1
协程
python3 将 asyncio 加入到了标准库。
import asyncioimport timeasync def test(num): await asyncio.sleep(num) print(num)async def run(): tasks = [asyncio.create_task(test(num)) for num in range(4)] [await t for t in tasks]def run_main(): asyncio.run(run())if __name__ == '__main__': run_main()
运行结果
import asyncioimport timeasync def test(num): await asyncio.sleep(num) print(num)async def run(): tasks = [asyncio.create_task(test(num)) for num in range(4)] [await t for t in tasks]def run_main(): asyncio.run(run())if __name__ == '__main__': run_main()
总结
以上就是本节的所有内容,主要简单地讲解了关于 进程、线程和协程 的概念和例子。