Skip to content

多任务

一、多进程

进程:CPU分配资源的最小单位,进程之间的内存是隔离的,子进程创建的时候会复制主进程的全局变量,不会共享。一个程序最少有一个进程,也叫做主进程

python
# 多进程创建:multiprocessing.Process
# 参数:
# group表示任务分组,官方文档目前对该属性不支持,默认为None
# target表示要执行的任务,传递一个函数名(核心)
# name表示进程名,只有显示意义
# args传递任务位置参数(用元组传)
# kwargs传递任务关键字参数(用字典传)
# daemon设置守护进程
# 执行进程 start
# 获取进程id - os.getpid()
# 获取进程父进程id - os.getppid()
# 强制关闭子进程 - terminate ()

# 示例:
def listen_music(name):
    for i in range(10):
        print(f'{name}正在听第{i + 1}首歌')
        time.sleep(1)


def work(name, age):
    for i in range(10):
        print(f'{age}岁的{name}正在摸第{i + 1}次鱼')
        time.sleep(1)


if __name__ == '__main__':
    # 1.通过给子进程设置守护进程来实现 - daemon=True
    eat_process = multiprocessing.Process(target=listen_music, name='听歌', args=('小王',), daemon=True)
    drink_process = multiprocessing.Process(target=work, name='工作', kwargs={'name': '小李', 'age': 18}, daemon=True)

    eat_process.start()
    drink_process.start()

    for i in range(5):
        print(f'主进程正在运行第{i + 1}次')
        time.sleep(1)
    # 2.强制关闭子进程 - terminate ()
    # eat_process.terminate()
    # drink_process.terminate()

进程之间的数据传输 - 管道

python
# 子进程修改全局变量
import multiprocessing
import time

# 创建一个用于进程间通信的管道
queue = multiprocessing.Queue()


def put_queue(queue):
    # 向管道中写入数据
    for i in range(10):
        queue.put(i)
        time.sleep(0.5)


def get_queue(queue):
    # 读取管道中的数据
    for i in range(10):
        # 管道中的数据先进先出
        print(queue.get())
        time.sleep(0.5)


if __name__ == '__main__':
    # 创建进程
    put_process = multiprocessing.Process(target=put_queue, args=(queue,))
    get_process = multiprocessing.Process(target=get_queue, args=(queue,))
    # 启动进程
    put_process.start()
    get_process.start()

二、多线程

一个进程至少得有一个线程,也叫主线程,线程是CPU调度的最小单位

python
# 创建线程 - threading.Thread
# 启动线程 - start
# 参数:
# group表示任务分组,官方文档目前对该属性不支持,默认为None
# target表示要执行的任务,传递一个函数名(核心)
# name表示进程名,只有显示意义
# args传递任务位置参数(用元组传)
# kwargs传递任务关键字参数(用字典传)
# daemon设置守护线程(要守护全守护)
# 示例:
import time
import threading


def listen_music(name):
    for i in range(10):
        print(f'{name}正在听第{i + 1}首歌')
        time.sleep(0.5)


def work(name, age):
    for i in range(10):
        print(f'{age}岁的{name}正在摸第{i + 1}次鱼')
        time.sleep(0.5)


if __name__ == '__main__':
    # 创建线程
    t1 = threading.Thread(target=listen_music, args=('小王',), daemon=True)
    t2 = threading.Thread(target=work, kwargs={'name': '小李', 'age': 18}, daemon=True)
    # 启动线程
    t1.start()
    t2.start()
    for i in range(5):
        print(f'主进程正在运行第{i + 1}次')
        time.sleep(0.5)

线程共享全局变量,修改全局变量用global关键字,但有时会出问题,用互斥锁解决

python
# 互斥锁
# 只有一把锁
# threading.Lock()
# 上锁lock.acquire()    解锁lock.release() 一般不用
# with lock:       常用
# 上锁会保证数据安全性,但会降低运行效率
# 全局解释器锁GIL - Global Interpreter Lock:python里一个进程里的多线程只能被一个cpu执行
# 死锁:只上了锁,无法解锁
import threading
import time

num = 4


def fn(lock):
    # lock.acquire()
    with lock:
        global num
        if num > 0:
            time.sleep(1)
            num -= 1
            print(f'{threading.current_thread().name}正在抢第{num + 1}张票')
    # lock.release()


if __name__ == '__main__':
    # 创建互斥锁
    lock = threading.Lock()
    p1 = threading.Thread(target=fn, name='线程1', args=(lock,))
    p2 = threading.Thread(target=fn, name='线程2', args=(lock,))
    p3 = threading.Thread(target=fn, name='线程3', args=(lock,))
    p4 = threading.Thread(target=fn, name='线程4', args=(lock,))
    p5 = threading.Thread(target=fn, name='线程5', args=(lock,))
    p6 = threading.Thread(target=fn, name='线程6', args=(lock,))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()
    p6.start()
    # 等待线程执行
    p1.join()
    p2.join()
    p3.join()
    p4.join()
    p5.join()
    p6.join()

    print(f'还剩{num}张票')

三、线程与进程的区别

  1. 进程

    进程 - CPU分配资源的最小单位 - 多个CPU就可以同时处理多个进程(CPU足够多的情况下,并行)

    进程可以独立关闭,适合使用守护进程的场景

  2. 线程

    线程 - CPU调度的最小单位 - 多个CPU即使有空闲,因为进程被1个CPU锁定,导致其他CPU不能进行支援(GIL)

四、多协程

用户自己调度的任务,切换由程序员(或事件循环)在应用层控制(开销极小)