人工智能-Python多任务编程-进程、线程

发布于:2025-02-11 ⋅ 阅读:(63) ⋅ 点赞:(0)
多任务的实现方式
  • 多进程

  • 多线程

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密集型应用(文件、网络),资源开销小,因为只需要进程中的一点点资源就可以运行

  • 缺点:不能使用多核