多任务的实现方式
多进程
多线程
1 多任务的两种表现形式
- 并发: 在一段时间内交替去执行多个任务(任务数大于CPU核心数)
- 并行: 在一段时间内真正的同时一起执行多个任务(任务数小于等于CPU核心数)
2 进程
进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位
一个程序运行后至少有一个进程
2.1 多进程-概念版
import multiprocessing
import time
def music():
for index in range(5):
print('听音乐')
time.sleep(0.5)
def code():
for index in range(5):
print('敲代码')
time.sleep(0.5)
if __name__ == '__main__':
# name 进程名称,一般无需定义,这里只是参考教学
# target 执行的目标任务名,这里指的是函数名(方法名)
# group 进程组,目前只能使用None
music_process = multiprocessing.Process(name='音乐进程', target=music, group=None)
code_process = multiprocessing.Process(name='代码进程', target=code, group=None)
music_process.start()
code_process.start()
2.2 多进程-参数版
import multiprocessing
import time
def music(times):
for index in range(times):
print('听音乐')
time.sleep(0.5)
def code(times):
for index in range(times):
print('敲代码')
time.sleep(0.5)
if __name__ == '__main__':
# name 进程名称,一般无需定义,这里只是参考教学
# target 执行的目标任务名,这里指的是函数名(方法名)
# group 进程组,目前只能使用None
# 方案一
# 元组方式传参 :元组方式传参一定要和参数的顺序保持一致
music_process = multiprocessing.Process(name='音乐进程', target=music, group=None, args=(10, ))
# 方案二
# 字典方式传参:字典方式传参字典中的key一定要和参数名保持一致
code_process = multiprocessing.Process(name='代码进程', target=code, group=None, kwargs={'times': 5})
music_process.start()
code_process.start()
2.3 多进程-进程、父进程ID
import multiprocessing
import os
import time
def handle():
# 获取当前进程
# 方法一
print(f'<多进程任务-函数>自定义进程的ID: {os.getpid()}')
# 方法二
print(f'<多进程任务-函数>自定义进程的ID: {multiprocessing.current_process().pid}')
# 获取父进程
print(f'<XX.py>自定义进程的父进程ID: {os.getppid()}')
if __name__ == '__main__':
code_process = multiprocessing.Process(target=handle)
code_process.start()
# 获取当前进程
# 方法一
print(f'<XX.py>当前执行文件的进程ID: {os.getpid()}')
# 方法二
print(f'<XX.py>当前执行文件的进程ID: {multiprocessing.current_process().pid}')
# 获取当前线程父进程 PPID
print(f'<Pycharm进程ID>当前执行文件的父进程ID: {os.getppid()}')
time.sleep(50)
2.4 多进程-杀掉进程
import os
def handle():
# 杀掉当前进程-多进程任务
os.kill(os.getpid(), 9)
if __name__ == '__main__':
# 杀掉主进程-XX.py(执行完此代码会终止下面代码的执行,所以想要杀掉Pycharm进程需要注释本行)
os.kill(os.getpid(), 9)
# 杀掉父进程-Pycharm
os.kill(os.getppid(), 9)
2.5 多进程-注意事项
2.5.1 进程之间不共享全局变量
创建子进程会对主进程资源进行拷贝
也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已
2.5.2 主进程默认不会等待所有的子进程执行结束再结束
目的
主进程结束时,立马停掉子进程
解决方案
1 设置守护进程
2 强制销毁子进程
import multiprocessing
import time
def music():
for index in range(10):
print('正在播放音乐中......')
time.sleep(0.5)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music)
music_process.start()
print('主线程执行结束')
2.5.2.1 设置守护进程
import multiprocessing
import time
def music():
for index in range(10):
print('正在播放音乐中......')
time.sleep(0.5)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music)
# 设置守护进程
music_process.daemon = True
music_process.start()
time.sleep(1)
print('主线程执行结束')
2.5.2.2 强制销毁子进程
import multiprocessing
import time
def music():
for index in range(10):
print('正在播放音乐中......')
time.sleep(0.5)
if __name__ == '__main__':
music_process = multiprocessing.Process(target=music)
music_process.start()
time.sleep(1)
# 强制销毁子进程
music_process.terminate()
print('主线程执行结束')
3 线程
线程是程序执行的最小单位,进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序
线程自己不拥有系统资源,但同属一个进程的多个线程共享进程所拥有的全部资源
3.1 多线程-概念版
import threading
import time
def music():
for index in range(10):
print('正在播放音乐中......')
time.sleep(0.5)
def code():
for index in range(10):
print('正在写代码中......')
time.sleep(0.5)
if __name__ == '__main__':
# target 执行的目标任务名,这里指的是函数名(方法名)
# name 线程名,一般不用设置
# group 线程组,目前只能使用 None
music_thread = threading.Thread(target=music, name='音乐线程', group=None)
music_thread.start()
code_thread = threading.Thread(target=code, name='代码线程', group=None)
code_thread.start()
3.2 多线程-参数版
import threading
import time
def music(times):
for index in range(times):
print('正在播放音乐中......')
time.sleep(0.5)
def code(times):
for index in range(times):
print('正在写代码中......')
time.sleep(0.5)
if __name__ == '__main__':
# target 执行的目标任务名,这里指的是函数名(方法名)
# name 线程名,一般不用设置
# group 线程组,目前只能使用 None
music_thread = threading.Thread(target=music, name='音乐线程', group=None, args=(3, ))
music_thread.start()
code_thread = threading.Thread(target=code, name='代码线程', group=None, kwargs={'times': 3})
code_thread.start()
3.3 多线程-设置守护线程
问题
主线程不会等待所有的子线程执行结束后主线程再结束
解决方案
要想主线程不等待子线程执行完成可以设置守护主线程
import threading
import time
def music(times):
for index in range(times):
print('正在播放音乐中......')
time.sleep(0.5)
def code(times):
for index in range(times):
print('正在写代码中......')
time.sleep(0.5)
if __name__ == '__main__':
# 设置守护线程-方案一
music_thread = threading.Thread(target=music, name='音乐线程', group=None, args=(10, ), daemon=True)
music_thread.start()
code_thread = threading.Thread(target=code, name='代码线程', group=None, kwargs={'times': 10})
# 设置守护线程-方案二
code_thread.daemon = True
code_thread.start()
time.sleep(1)
print('主线程执行完毕')
4 进程、线程的对比
4.1 关系对比
线程是依附在进程里面的,没有进程就没有线程
一个进程默认提供一条线程,进程可以创建多个线程
4.2 区别对比
进程之间不共享全局变量
线程之间共享全局变量
创建进程的资源开销要比创建线程的资源开销要大
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
线程不能够独立执行,必须依存在进程中
4.3 优缺点对比
进程优缺点
优点:适合CPU密集型应用,因为其可以用多核
缺点:资源开销大
线程优缺点
优点:适合IO密集型应用(文件、网络),资源开销小,因为只需要进程中的一点点资源就可以运行
缺点:不能使用多核