一、进程与线程
进程的概念
对于操作系统来说,一个任务就是一个进程,进程就是程序执行的载体,如Python脚本中执行main函数就启动了一个进程,打开微信或者浏览器就是开启了一个进程,进程的运行需要资源支持,也就需要消耗CPU和内存
PID是各进程的代号,每个进程有唯一的PID编号
多进程就是操作系统同时运行多个进程,比如一边用Chrome上网一边听音乐一边在进行上传文件,这就是多进程(任务),至少同时有3个任务同时运行
线程的概念
进程由线程组成,线程是执行任务逻辑的角色,进程提供线程执行程序的前置要求,线程在重组的资源配备下执行程序
打开一个浏览器就是启动一个进程,并且获取足够的资源,通过主进程中的主线程执行业务逻辑,主线程创建多个线程也就是对应了浏览器的多个tab页,线程之间互不影响,线程之间共享资源,显然常见多线程要比创建多进程节省资源
二、多进程
多进程的创建与使用
创建进程需要使用到multiprocessing模块,该模块中的常用函数有:
- Process:创建一个进程,返回一个对象
- start:执行进程,无参数、无返回值
- join:阻塞程序,无参数、无返回值
- kill:杀死进程,无参数、无返回值
- is_alive:判断今晨个是否存活,返回布尔值
import time
def alpha():
for i in range(10):
print(i, 'alpha')
time.sleep(1)
def bravo():
for i in range(10):
print(i, 'bravo')
time.sleep(1)
if __name__ == '__main__':
start = time.time()
alpha()
bravo()
end = time.time()
print("执行了{}".format(end - start))
复制代码
导入os模块,分别在两个for循环以及main函数中打印出PID
print('PID:{}'.format(os.getpid()))
复制代码
根据控制台的打印,可以说明两个for循环是在同一个进程中执行的,并且是先执行alpha中的for循环再执行bravo中的for循环,所以整个程序耗时较长
使用多进程可以提高程序的执行效率,在程序中导入多进程模块multiprocessing,修改main函数,创建新的进程来执行alpha函数
if __name__ == '__main__':
start = time.time()
# 使用多继承执行alpha
alpha_p = multiprocessing.Process(target=alpha)
alpha_p.start()
# alpha()
bravo()
end = time.time()
print("执行了{}".format(end - start))
print('主PID为:{}'.format(os.getpid()))
复制代码
两个for循环几乎同时执行完成,bravo是在主进程上执行的,而alpha是在其他进程执行的,两个函数的PID是不同的,所以总的执行时间缩短了一半
再创建一个进程来执行bravo函数,目前程序中存在三个进程,分别是执行alpha的进程、执行bravo的进程和执行main函数的进程
if __name__ == '__main__':
start = time.time()
# 使用多继承执行alpha
alpha_p = multiprocessing.Process(target=alpha)
alpha_p.start()
# alpha()
# bravo()
bravo_p = multiprocessing.Process(target=bravo)
bravo_p.start()
end = time.time()
print("执行了{}".format(end - start))
print('主PID为:{}'.format(os.getpid()))
复制代码
子进程和主进程之间互不影响,所以时间差非常短,但是我们希望这个时间差是从开始执行到执行结束所耗费的时间,并不是main进程启动后就执行
修改main函数中的代码
if __name__ == '__main__':
start = time.time()
# 使用多继承执行alpha
alpha_p = multiprocessing.Process(target=alpha)
# alpha_p.start()
# alpha()
# bravo()
bravo_p = multiprocessing.Process(target=bravo)
# bravo_p.start()
for p in (alpha_p, bravo_p):
p.start()
for p in (alpha_p, bravo_p):
p.join()
end = time.time()
print("执行了{}".format(end - start))
print('主PID为:{}'.format(os.getpid()))
复制代码
子进程结束之后再去执行主进程
注释for循环,在a子进程执行完之后,调用join()函数,在调用b函数
if __name__ == '__main__':
start = time.time()
# 使用多继承执行alpha
alpha_p = multiprocessing.Process(target=alpha)
alpha_p.start()
# 阻塞子进程
alpha_p.join()
# alpha()
# bravo()
bravo_p = multiprocessing.Process(target=bravo)
bravo_p.start()
# 执行两个函数
# for p in (alpha_p, bravo_p):
# p.start()
# # 阻塞两个子进程
# for p in (alpha_p, bravo_p):
# p.join()
end = time.time()
print("执行了{}".format(end - start))
print('主PID为:{}'.format(os.getpid()))
复制代码
此时的时间差是alpha函数执行的耗时,alpha函数执行完成之后,bravo函数才开始执行
关闭alpha_p和bravo_p的执行,再增加一个for循环,打印出进程是否存活
if __name__ == '__main__':
start = time.time()
# 使用多继承执行alpha
alpha_p = multiprocessing.Process(target=alpha)
# alpha_p.start()
# 阻塞子进程
# alpha_p.join()
# alpha()
# bravo()
bravo_p = multiprocessing.Process(target=bravo)
# bravo_p.start()
# 执行两个函数
for p in (alpha_p, bravo_p):
p.start()
# 阻塞两个子进程
for p in (alpha_p, bravo_p):
p.join()
# 判断进程是否存活
for p in (alpha_p, bravo_p):
print('is alive:{}'.format(p.is_alive()))
end = time.time()
print("执行了{}".format(end - start))
print('主PID为:{}'.format(os.getpid()))
复制代码
在alpha和bravo执行完成之后,两个进程都已经关闭
多线程的优点是缩短脚本执行时间,提高执行效率
多进程存在的问题有
- 通过进程模块执行的函数无法获取返回值
- 多个今进程同时修改文件可能会出现错误
- 进程数量太多会造成资源不足、死机的情况
进程池
进程池的概念与数据库连接池的概念是类似的,都是为了提高效率,避免线程创建于关闭的消耗
多进程模块multiprocessing中进程池的相关函数
- Pool:进程池的创建,参数为要创建的进程的个数,返回一个进程池对象
- applu_async:任务加入线程池(异步),参数函数名和函数的参数,无返回值
- close:关闭进程池,无参数、无返回值
- join:等待进程池任务结束,无参数、无返回值
import multiprocessing
import os
import time
def alpha(count):
print(count, os.getpid())
time.sleep(5)
if __name__ == '__main__':
pool = multiprocessing.Pool(5)
for i in range(20):
pool.apply_async(func=alpha, args=(i,))
time.sleep(20)
复制代码
进程被重复利用了,这里调用了异步,异步就是非同步,导致前后使用的进程号顺序不一致
进程池结束任务之前,主进程就已经结束了,程序结束,进程池就被关闭了
pool.close()
pool.join()
复制代码
在time.sleep()函数下添加代码,并注释time.sleep()函数 20个任务全部完成,需要通过close()函数和join()函数,来保证在子线程执行结束之后,再结束主线程,在退出程序
alpha()函数添加return, 异步是可以获取返回值的
import multiprocessing
import os
import time
def alpha(count):
print(count, os.getpid())
time.sleep(5)
return 'result is %s, pid is %s' % (count, os.getpid())
if __name__ == '__main__':
pool = multiprocessing.Pool(5)
res_list = []
for i in range(20):
res = pool.apply_async(func=alpha, args=(i, ))
res_list.append(res)
for res in res_list:
print(res.get())
# time.sleep(20)
# pool.close()
# pool.join()
复制代码
第一组先执行,执行完成之后打印出结果,同时第二组也开始执行
进程锁
当一个进程开始执行任务的时候,为了避免进程被其他任务使用,需要通过锁开控制,只有解锁之后才能执行下一个任务
进程锁相关的函数: acquire:上锁,无参数、无返回值 release:开锁,无参数、无返回值
import multiprocessing
import os
import time
def alpha(count, lock):
# 上锁
lock.acquire()
print(count, os.getpid())
time.sleep(5)
# 解锁
lock.release()
return 'result is %s, pid is %s' % (count, os.getpid())
if __name__ == '__main__':
pool = multiprocessing.Pool(5)
manager = multiprocessing.Manager()
lock = manager.Lock()
res_list = []
for i in range(20):
res = pool.apply_async(func=alpha, args=(i, lock))
# res_list.append(res)
pool.close()
pool.join()
复制代码
每次只有一个进程在工作,锁不可以滥用,锁没有解开就会造成死锁现象