线程与进程的区别
- 根本区别:
- 进程:操作系统资源分配的基本单位
- 线程:任务调度和执行的基本单位
- 开销:
- 进程:通过复制代码 + 资源创建子进程 每个子进程都有独立的代码和数据 空间,进程间切换需要有较大的开销
- 线程:在同一份代码里,创建线程,共享资源,开销较小
- 分配内存:
- 进程:系统在运行的时候为每个进程分配好不同的内存空间
- 线程:线程所使用的资源是它所属的进程资源
- 包含关系:
- 进程:一个进程需要有多个线程
- 线程:线程是进程的一部分
进程的介绍
- 进程的组成:
- 程序
- 数据集
- 进程控制模块
- 进程是资源的结合,是最小的资源单位。是一个程序在一个数据集上的一次动态执行过程。我们编写的程序是用来描述进程执行哪些操作和如何完成的;数据集则是程序在执行中所使用的资源;进程控制模块是用来记录进程的外部特征,描述进程的执行的变化过程,系统可以用它来控制和管理进程,它是系统感知进程存在的唯一标志。
- 并行处理:在同一时刻,有多条指令在多个处理器上同时执行。 并行处理的目的是节省大型和复杂问题的时间
- 并发处理:同一时刻只能执行一条指令,但多个进程指令被快速的轮换执行使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
无论是并行还是并发,在用户看来都是’同时’运行的,不管是进程还是线程,真是干活的是cpu,而一个cpu同一时刻只能执行一个任务。对于“并发”而言,是伪并行,即看起来是同时运行,单个cpu+多道技术就可以实现并发;而“并行”才是真正意义上的“同时运行”——仅有多核才能够实现“并行”。采用多进程可以实现并行的效果,充分的使用多核CPU的资源,对IO密集型的操作可以很好利用IO阻塞的时间。
进程的创建
-
python中的多进程是使用multiprocessing模块实现,mutiprocessing模块支持创建子进程,并在子进程中执行各自的任务;支持数据共享和通信;支持不同形式的同步;提供Process、Manager、Pool、Queue、Lock等组件
-
Process 模块的方法有:
- start()启动进程,并调动改进程中的run()方法
- run():进程启动时运行的方法,它去调用target指定的函数,我们自定义类的 类中一定要实现该方法
- terminate():强制终止进程。不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程。
- join:在当前位置阻塞主进程,等待执行join的子进程结束后再继续执行主进程,与线程join方法一样
-
Process模块的参数有:
- target:调用对象,指子进程要执行的任务
- name:子进程的名字
- group:默认值为None
- args:以元组形式传递位置参数
- kwargs:以字典形式传递参数
子进程创建的两种方法:
# 函数方法
import multiprocessing
def demo1():
pass
def demo2():
pass
def main():
m1 = multiprocessing.Process(target=demo1)
m2 = multiprocessing.Process(target=demo2)
m1.start()
m2.start()
if __name__ == '__main__':
main()
# 通过继承类来创建进程
class demo1(multiprocessing.Process):
def __init__(self):
super().__init__()
def run(self) -> None:
pass
class demo2(multiprocessing.Process):
def __init__(self):
super(demo2, self).__init__()
def run(self) -> None:
pass
if __name__ == '__main__':
m1 = demo1()
m2 = demo2()
m1.start()
m2.start()
子进程与主进程的执行
- dameon 和 join 方法
import multiprocessing
import time
def demo1():
for i in range(4):
print('--demo1--')
time.sleep(1)
def main():
m1 = multiprocessing.Process(target=demo1)
m1.daemon = True
m1.start()
print('--main--')
if __name__ == '__main__':
main()
# --main--
def demo1():
for i in range(4):
print('--demo1--')
time.sleep(1)
def main():
m1 = multiprocessing.Process(target=demo1)
m1.start()
m1.join()
print('--main--')
if __name__ == '__main__':
main()
# --demo1--
# --demo1--
# --demo1--
# --demo1--
# --main--
进程间是否共享全局变量
# 线程间共享全局变量
import threading
num = 100
def demo1(nums):
global num
for i in range(nums):
num += 1
print('--demo1--{}'.format(num))
def demo2(nums):
global num
for i in range(nums):
num += 1
print('--demo2--{}'.format(num))
if __name__ == '__main__':
t1 = threading.Thread(target=demo1, args=(100,))
t2 = threading.Thread(target=demo2, args=(100,))
t1.start()
t2.start()
print('-main--{}'.format(num))
# --demo1--200
# --demo2--300
# -main--300
# 进程间不共享全局变量
import multiprocessing
num = 100
def demo1(nums):
global num
for i in range(nums):
num +=1
print('--demo1--{}'.format(num))
def demo2(nums):
global num
for i in range(nums):
num += 1
print('--demo2-{}'.format(num))
if __name__ == '__main__':
m1 = multiprocessing.Process(target=demo1, args=(100,))
m2 = multiprocessing.Process(target=demo2, kwargs={'nums': 100})
m1.start()
m2.start()
m1.join()
m2.join()
print('--main--{}'.format(num))
# --demo1--200
# --demo2-200
# --main--100
由上所述:进程间是不会共享全局变量的,这时候需要用到队列进行通信
start() 与 run()的区别
- start()是用来启动进程的,真正实现了多进程运行,这是无需等待run()方法体,代码执行完毕后直接执行下面的代码:调用Process 类的start() 方法来启动一个线程,这是此进程处于就绪状态,并没有运行,一旦得到 CPU时间片,就开始执行run()方法,这里的run方法为进程体,当进程结束后,不可以重新启动
- run()方法只是类的一个普通方法,如果直接调用 run()方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条, 还是按顺序执行,还是要等待run()方法体执行完毕后,才继续执行下面的代码,这样就没有达到写线程的目的。
进程池
- 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process 动态生成多个进程,但如果是成千上百个目标,手动的去创建进程工作量巨大,此时就可以用到multiprocessing模块提供的Pool 方法,也就是进程池。
初始化 Pool 时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求,但是如果进程池中的进程已经达到指定的最大值,那么该请求 就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。
from multiprocessing import Pool
import time
import os
def worker(msg):
start = time.time()
print(f"{msg}开始执行,进程号为{os.getpid()}")
time.sleep(2)
end = time.time()
print(msg, '执行完成,耗时{}'.format(end - start))
if __name__ == '__main__':
# 创建进程池,并确定进程池的大小
po = Pool(3)
for i in range(10):
# 对进程池添加任务
po.apply_async(worker, (i,))
# 关闭进程池,不再接受新的进程
po.close()
po.join()
"""
使用进程池之后,就不需要在启动进程了,因为运行进程池的时候,进程已经启动了
"""
进程池间的通信
- 进程池间的通信需要使用队列
- multiprocessing.Manager().Queue()方法
import multiprocessing
from multiprocessing import Pool
def demo1(q):
q.put('a')
def demo2(q):
q.put('b')
if __name__ == '__main__':
# 创建队列
q = multiprocessing.Manager().Queue()
po = Pool(2)
# 向进程池中添加任务,与实例化进程的target类似
po.apply_async(demo1, args=(q,))
po.apply_async(demo2, args=(q,))
po.close()
po.join()
print(q.get())
print(q.get())
# a
# b