主题
多任务
一、多进程
进程: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}张票')三、线程与进程的区别
进程
进程 - CPU分配资源的最小单位 - 多个CPU就可以同时处理多个进程(CPU足够多的情况下,并行)
进程可以独立关闭,适合使用守护进程的场景
线程
线程 - CPU调度的最小单位 - 多个CPU即使有空闲,因为进程被1个CPU锁定,导致其他CPU不能进行支援(GIL)
四、多协程
用户自己调度的任务,切换由程序员(或事件循环)在应用层控制(开销极小)