
系列文章目录
[Python]目录
视频及资料和课件
链接:https://pan.baidu.com/s/1LCv_qyWslwB-MYw56fjbDg?pwd=1234
提取码:1234
利用现学知识能够让两个函数或者方法同时执行吗?
不能,因为之前所写的程序都是单任务的,也就是说一个函数或者方法执行完成另外一个函数或者方法才能执行,要想实现这种操作就需要使用多任务。
多任务的最大好处是充分利用CPU资源,提高程序的执行效率。
多任务是指在同一时间内执行多个任务,例如: 现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。
多任务效果图:

在一段时间内交替去执行任务。
例如:
对于单核cpu处理多任务,操作系统轮流让各个软件交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒……这样反复执行下去。表面上看,每个软件都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像这些软件都在同时执行一样,这里需要注意单核cpu是并发的执行多任务的。
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行。
并行才是多个任务真正意义一起执行。
对于多核CPU,如果执行的任务数小于CPU的核数,则并行执行;如果执行的任务数大于CPU的核数,则并发执行。
在Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式。
一个正在运行的程序或者软件就是一个进程,进程是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。
进程向操作系统索要内存资源,保证进程可以正常运行。真正在执行代码的是线程。
例如,将社会看成是操作系统,社会中的各种资源尤其管理,现实生活中的公司可以理解成是一个进程,公司通过向社会获取资源,从而能够向员工提供办公资源(电脑、办公桌椅等),然而在公司中真正干活的是员工,员工可以理解成线程。
注意:
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程。
单进程效果图:

多进程效果图:

说明:
多进程可以完成多任务,每个进程就好比一家独立的公司,每个公司都各自在运营,每个进程各自在运行,执行各自的任务。
#导入进程包
import multiprocessing
group:指定进程组,目前只能使用None

target:执行的目标任务名(需要执行的函数或方法的函数名或方法名)
name:进程名字(一般不用设置,每个进程会有默认的进程名)
args:以元组方式给执行任务传参
kwargs:以字典方式给执行任务传参
# 导入进程包
import multiprocessing
import time
# 唱歌任务
def sing():
for i in range(5):
print('唱歌中...')
time.sleep(0.2)
# 跳舞任务
def dance():
for i in range(5):
print('跳舞中...')
time.sleep(0.2)
# 判断当前运行的是否为主进程
if __name__ == '__main__':
# 创建跳舞的子进程
dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
# 启动跳舞的子进程
dance_process.start()
# 主进程执行唱歌的任务
sing()
注意:
执行的目标任务后面不能带(),否则相当于调用函数执行。
进程的执行顺序是无序的,具体哪个进程先执行由操作系统调度决定。
获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。
获取进程编号的两种操作:
(1)获取当前进程编号
(2)获取当前父进程编号
os.getpid() 表示获取当前进程编号
# 获取当前进程对象
multiprocessing.current_process()
示例代码:
# 导入进程包
import multiprocessing
import time
# 导入获取进程编号相关的模块
import os
# 唱歌任务
def sing():
# 获取当前进程的id
sing_process_id = os.getpid()
# 获取当前进程对象
sing_process_object = multiprocessing.current_process()
print(sing_process_object, sing_process_id)
for i in range(5):
print('唱歌中...')
time.sleep(0.2)
# 跳舞任务
def dance():
# 获取当前进程的id
dance_process_id = os.getpid()
# 获取当前进程对象
dance_process_object = multiprocessing.current_process()
print(dance_process_object, dance_process_id)
for i in range(5):
print('跳舞中...')
time.sleep(0.2)
# 判断当前运行的是否为主进程
if __name__ == '__main__':
# 获取当前进程的id
main_process_id = os.getpid()
# 获取当前进程对象
main_process_object = multiprocessing.current_process()
print(main_process_object, main_process_id)
# 创建跳舞的子进程
dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
# 启动跳舞的子进程
dance_process.start()
# 主进程执行唱歌的任务
sing()
由于任务 sing 运行在主进程中,所以 sing 任务中获取的进程对象及进程编号与主进程相同。
主进程的进程名为 MainProcess
dance 任务的进程名为dance是由于创建进程时,由指定进程名。
os.getppid() 表示获取当前父进程编号
示例代码:
# 导入进程包
import multiprocessing
import time
# 导入获取进程编号相关的模块
import os
# 唱歌任务
def sing():
print('sing(): ')
# 获取当前进程的id
sing_process_id = os.getpid()
# 获取当前进程对象
sing_process_object = multiprocessing.current_process()
print(sing_process_object, sing_process_id)
print('父进程编号:', os.getppid())
for i in range(5):
print('唱歌中...')
time.sleep(0.2)
# 跳舞任务
def dance():
print('dance(): ')
# 获取当前进程的id
dance_process_id = os.getpid()
# 获取当前进程对象
dance_process_object = multiprocessing.current_process()
print(dance_process_object, dance_process_id)
print('父进程编号:', os.getppid())
for i in range(5):
print('跳舞中...')
time.sleep(0.2)
# 判断当前运行的是否为主进程
if __name__ == '__main__':
print('main: ')
# 获取当前进程的id
main_process_id = os.getpid()
# 获取当前进程对象
main_process_object = multiprocessing.current_process()
print(main_process_object, main_process_id)
# 创建跳舞的子进程
dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
# 创建唱歌的子进程
sing_process = multiprocessing.Process(target=sing, name='sing')
# 启动子进程
dance_process.start()
sing_process.start()
语法:
进程对象.name
# 导入进程包
import multiprocessing
import time
# 导入获取进程编号相关的模块
import os
# 唱歌任务
def sing():
print('sing(): ')
# 获取当前进程对象
sing_process_object = multiprocessing.current_process()
print('进程名:', sing_process_object.name)
for i in range(5):
print('唱歌中...')
time.sleep(0.2)
# 判断当前运行的是否为主进程
if __name__ == '__main__':
print('main: ')
# 获取当前进程对象
main_process_object = multiprocessing.current_process()
print('进程名:', main_process_object.name)
# 创建唱歌的子进程
sing_process = multiprocessing.Process(target=sing, name='sing')
# 启动子进程
sing_process.start()
语法
import os
# 第二个参数为杀死进程的方式,9 -- 强制杀死进程
os.kill(要杀死的进程编号, 9)
# 导入进程包
import multiprocessing
import time
# 导入获取进程编号相关的模块
import os
# 唱歌任务
def sing():
print('sing(): ')
# 获取当前进程对象
sing_process_object = multiprocessing.current_process()
print('进程名:', sing_process_object.name)
for i in range(5):
print('唱歌中...')
time.sleep(0.2)
os.kill(os.getpid(), 9)
# 判断当前运行的是否为主进程
if __name__ == '__main__':
print('main: ')
# 获取当前进程对象
main_process_object = multiprocessing.current_process()
print('进程名:', main_process_object.name)
# 创建唱歌的子进程
sing_process = multiprocessing.Process(target=sing, name='sing')
# 启动子进程
sing_process.start()
Process类执行任务并给任务传参数有两种方式:
(1)args 表示以元组的方式给执行任务传参
(2)kwargs 表示以字典方式给执行任务传参
元组方式传参(args): 元组方式传参一定要和参数的顺序保持一致。
使用元组的方式进行传参,如果参数只有一个,元组的第一个逗号不能省略。
import multiprocessing # 显示信息的任务 def show_info(name, age): print(name, age) if __name__ == '__main__': # 创建子进程 # 向任务传参使用 args 元组方式传参 show_info_process = multiprocessing.Process(target=show_info, args=('zs', 20)) # 启动子进程 show_info_process.start()'运行
字典方式传参(kwargs): 字典方式传参字典中的key一定要和参数名保持一致。
import multiprocessing # 显示信息的任务 def show_info(name, age): print(name, age) if __name__ == '__main__': # 创建子进程 # 向任务传参使用 args 元组方式传参 show_info_process = multiprocessing.Process(target=show_info, kwargs={'name': 'zs', 'age': 29}) # 启动子进程 show_info_process.start()'运行
第一个参数的传递使用元组,第二个参数的传递使用字典:
import multiprocessing # 显示信息的任务 def show_info(name, age): print(name, age) if __name__ == '__main__': # 创建子进程 # 向任务传参使用 args 元组方式传参 show_info_process = multiprocessing.Process(target=show_info, args=('zs',), kwargs={'age': 29}) # 启动子进程 show_info_process.start()'运行
(1)进程之间不共享全局变量
(2)主进程会等待所有的子进程执行结束再结束
创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,是不同进程里面名字相同的全局变量。
Windows下多进程程序,如果在主进程中没有
if __name__ == '__main__':,会报错,因为创建子进程会对主进程资源进行拷贝,也就是说,子进程的代码与主进程一模一样,当主进程调用子进程时,由于子进程中存在直接执行新建子进程和调用子进程的代码,会出现无限递归调用,所以会报错。如果加上if __name__ == '__main__':判断,不是主进程就不会执行新建子进程和调用子进程的代码。
import multiprocessing import time # 定义全局变量 g_list = list() # 添加数据的任务 def add_data(): for i in range(5): g_list.append(i) print("add:", i) time.sleep(0.2) # 代码执行到此,说明数据添加完成 print("add_data:", g_list) def read_data(): print("read_data", g_list) if __name__ == '__main__': # 创建添加数据的子进程 add_data_process = multiprocessing.Process(target=add_data) # 创建读取数据的子进程 read_data_process = multiprocessing.Process(target=read_data) # 启动子进程执行对应的任务 add_data_process.start() # 主进程等待添加数据的子进程执行完成以后程序再继续往下执行,读取数据 add_data_process.join() read_data_process.start() print("main:", g_list) # 总结: 多进程之间不共享全局变量'运行
进程之间不共享全局变量的解释效果图:

假如我们现在创建一个子进程,这个子进程执行完大概需要2秒钟,现在让主进程执行0.5秒钟就退出程序,查看一下执行结果,示例代码如下:
import multiprocessing import time # 定义进程所需要执行的任务 def task(): for i in range(10): print("任务执行中...") time.sleep(0.2) if __name__ == '__main__': # 创建子进程 sub_process = multiprocessing.Process(target=task) sub_process.start() # 主进程延时0.5秒钟 time.sleep(0.5) print("over") exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出'运行
为了保证子进程能够正常的运行,主进程会等所有的子进程执行完成以后再销毁,设置守护主进程的目的是主进程退出子进程销毁,不让主进程再等待子进程去执行。
子进程对象.daemon = True子进程对象.terminate()在pycharm中可能执行不出效果,需要在终端执行代码。(Windows)
import multiprocessing import time # 定义进程所需要执行的任务 def task(): for i in range(10): print("任务执行中...") time.sleep(0.2) if __name__ == '__main__': # 创建子进程 sub_process = multiprocessing.Process(target=task) # 设置守护主进程,主进程退出子进程直接销毁,子进程的生命周期依赖与主进程 sub_process.daemon = True sub_process.start() time.sleep(0.5) print("over") # 总结: 主进程会等待所有的子进程执行完成以后程序再退出 # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁'运行
在pycharm中可能执行不出效果,需要在终端执行代码。(Windows)
import multiprocessing import time # 定义进程所需要执行的任务 def task(): for i in range(10): print("任务执行中...") time.sleep(0.2) if __name__ == '__main__': # 创建子进程 sub_process = multiprocessing.Process(target=task) sub_process.start() time.sleep(0.5) print("over") # 让子进程销毁 sub_process.terminate() exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出 # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁'运行