创作不易,各位看官,点个赞收藏、关注必回关、QAQ、您的点赞是我更新的最大动力!
1.电脑实现多任务的原理
例如qq、微信、网易云音乐播放器3个应用程序能同时运行是因为CPU在多个应用程序之间高速切换的结果,当CPU切换到了qq,就用0.01s时间(时间不确定)执行qq程序,然后再随机切换到其他应用程序在执行一段时间,CPU在多个程序之间快速往复执行,我们的肉眼根本感觉不到卡顿,导致我们的错觉感觉是同时运行的效果。如果电脑运行了多个程序有时候会出现卡顿现象是因为cup切换不过来了
2.单核,双核CPU介绍
3.查看CPU
电脑–>属性–>设备管理器–>处理器,有 4 个表示一个 CPU 中有 4 个核心
2.30GHz表示运算速度,越高越好。例如1s中可以计算多少次4.在Python中实现多任务可以使用进程和线程,但是在实际应用中多线程是使用最多的,包括后面我们要学习的爬虫,一般也都是使用线程,当然实际开发中基本都是使用多线程框架来去实现我们的任务,但是在面试过程中经常会被问道线程进程的区别,这里重点讲线程进程的区别和线程的使用。
(二)串行,并发,并行,同步,异步
1.串行
多个任务依次执行,上一个任务没有完成时不能执行后续的任务,最明显的同步执行过程
2.并发(同时出发,不同的人步调不一定一致)
多个任务依次执行,执行过程中多个任务可以替换执行,在某一个时刻是一个任务在执行,但是在某个时间段内是多个任务在执行。
3.并行(一起出行,不同的人步调一致—>一二三四)
多个任务没有顺序,同时执行,最终的执行结果跟耗时最长的任务有关
4.同步和异步
计算机中的多任务实现方式:
➔ 多进程实现的多任务操作:并行执行(默认)
➔ 多线程实现的多任务操作:并发执行(默认)
➔ 协程实现的多任务操作:事件处理并发执行(默认)
返回文章目录
(一)进程
进程:正在运行的一个程序,可以说是一个进程,是系统进行资源分配和调用的独立单元,每一个进程都有自己独立的内存空间和系统资源。
程序:运行起来的应用程序就称为进程,也就是当程序不运行的时候我们称为程序,当程序运行起来他就是一个进程,通俗的理解就是不运行的时候是程序,运行起来就是进程。程序只有一个,但是进程有多个
(二)线程
由于进程是资源拥有者,创建、撤消与切换存在较大的内存开销,因此需要引入轻型进程 即线程,进程是资源分配的最小单位,线程是 CPU 调度的最小单位(程序真正执行的时候调 用的是线程)每一个进程中至少有一个线程
(三)进程和线程之间的关系
打开酷狗 [ 程序],开闭空间操作[ 进程],听歌同时下载歌曲[ 2个线程]
返回文章目录
1.传统函数执行
无法实现同步进行,必须自顶向下的逻辑运行
# 一边下载歌曲,一边听歌
import time
def listen():
for i in range(1,6):
print("正在听歌")
time.sleep(1)
def down_load():
for i in range(1,6):
print("正在下载歌曲")
time.sleep(1)
down_load()
listen()
2.使用 threading 线程管理 [ 函数式 类方式]
可以实现,两个线程同时进行,解决了传统执行的尴尬,生活中我们经常,边下载歌曲,边听歌。或者边吃饭,边看手机
注意,结果根据处理,每次启动程序可能出现不同的先后顺序,并无关系
函数式线程使用、基本语法
import threading # 导入线程
创建两个线程
t1 = threading.Thread(target=test) #test 为函数名称
t2 = threading.Thread(target=test) #test 为函数名称
# 设置线程名称
t1.name = "线程1----------"
t2.name = "线程2"
# 启动线程
t1.start()
t2.start()
类型方式使用线程、基本语法
import threading # 导入线程
class Function(threading.Thread)
# 创建线程对象
t1 = Function()
t2 = Function()
# 启动线程
t1.start()
t2.start()
函数式
# 创建线程对象
t1 = MyCounter()
t2 = MyCounter()
# 启动线程
t1.start()
t2.start()
# 一边下载歌曲,一边听歌
import time
import threading
def listen():
for i in range(1,6):
print("正在听歌")
time.sleep(1)
def down_load():
for i in range(1,6):
print("正在下载歌曲")
time.sleep(1)
# down_load()
# listen()
if __name__ == '__main__':
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 执行线程
t1.start()
t2.start()
class MyCounter(threading.Thread):
"""自定义线程类型,继承threading.Thread类"""
# 类属性
num = 1
def run(self):
"""重写run方法,在线程start()启动后会自动执行"""
while True:
MyCounter.num += 1
print(f"{threading.current_thread().getName()} num1: {MyCounter.num}")
time.sleep(1)
# 不取名字有自己的默认名字
# 创建线程对象
t1 = MyCounter()
t2 = MyCounter()
# 启动线程
t1.start()
t2.start()
import threading
import time
def test1():
for i in range(1,6):
time.sleep(1)
print("--子线程1--%d"%i)
print("子线程1中查看线程情况:",threading.enumerate())
def test2():
for i in range(1,6):
time.sleep(1)
print("--子线程2--%d"%i)
print("子线程2中查看线程情况:",threading.enumerate())
def main():
print("创建线程之前的线程情况:",threading.enumerate())
#创建线程
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
time.sleep(1)
print("创建线程之后的线程情况:", threading.enumerate())
#开启线程
t1.start()
t2.start()
time.sleep(1)
print("调用start之后的线程情况:", threading.enumerate())
time.sleep(6)
print("等待子线程执行结束后的线程情况:", threading.enumerate())
if __name__ == '__main__':
main()
4.资源独占join()
线程资源独占方法独占 CPU 时间片
join()
功能:当前线程执行完之后,其它线程才执行
多线程 资源独占
1、程序中有主线程、子线程,但是多个线程的执行过程,没有顺序。
那个执行可以执行由CPU管理的!
2、子线程没有执行结束时,主线程不会退出
子线程没有执行结束时,主线程中的代码可以全部执行。
一直到所有的子线程全部运行完毕,主线程才能退出[当前程序]
3、线程资源独占:强制独占-当前时刻已经启动的线程占用所有资源
其他线程排队,提升部分线程的优先级,将并发执行变成同步执行
# 一边下载歌曲,一边听歌
import time
import threading
def listen(num):
for i in range(1,num):
print(f"正在听歌--{i}")
time.sleep(1)
def down_load(num,num2):
print(f"第二个参数{num2}")
for i in range(1,num):
print(f"正在下载歌曲--{i}")
time.sleep(1)
# down_load()
# listen()
def main():
# 新增需求:下载完歌曲,才能听歌
# 创建线程
t1 = threading.Thread(target=down_load,args=(5,6)) # args = (参数1,参数2)
t2 = threading.Thread(target=listen,args=(5,))
# 执行线程
t1.start()
t1.join() # 设定第一个线程,下载歌曲执行完才能 到线程二 听歌,并且 t1线程结束后 才执行主线程
t2.start()
t2.join() # t1,t2线程执行完后才回到主线程执行
if __name__ == '__main__':
main() # 线程 执行
print("程序执行结束了")
5.守护线程 setDaemon()
setDaemon()将当前线程设置成守护线程来守护主线程
当主线程结束后,守护线程也就结束,不管是否执行完成
应用场景: qq 多个聊天窗口,就是守护线程
注意,需要在子线程开启的时候设置成守护线程,否则无效
主线程必须要等待子线程结束了,才能退出吗?
如 QQ 软件(主线程):聊天窗口(子线程);直接关闭 QQ 主窗口,同时自动关闭聊天窗口?
好像主线程没有等待子线程结束,而是自己结束的时候,不论子线程是否结束直接退出!
主线程如果不等待子线程运行结束,可以直接结束并自动关闭任何状态的子线程
此时:该主线程可以关闭的子线程:是属于自己的守护线程!
需求:用户正在听歌和下载歌曲,关闭QQ音乐软件,应该伴随软件关闭,音乐和下载都关闭
没有守护线程效果
"""
setDaemon()
守护线程
"""
# 一边下载歌曲,一边听歌
import time
import threading
def listen():
for i in range(1,6):
print("正在听歌")
time.sleep(1)
def down_load():
for i in range(1,6):
print(f"正在下载歌曲")
time.sleep(1)
def main():
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 执行线程
t1.start()
t2.start()
if __name__ == '__main__':
main()
print("QQ音乐软件关闭了") # 主程序
"""
setDaemon()
守护线程
"""
# 一边下载歌曲,一边听歌
import time
import threading
def listen():
for i in range(1,6):
print("正在听歌")
time.sleep(1)
def down_load():
for i in range(1,6):
print(f"正在下载歌曲")
time.sleep(1)
def main():
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 守护线程
t1.setDaemon(True)
t2.setDaemon(True)
# 执行线程
t1.start()
t2.start()
if __name__ == '__main__':
main()
print("QQ音乐软件关闭了") # 主程序
1、将下载歌曲线程设置成 6 次,听歌线程设置 3次 """
setDaemon()
守护线程
"""
# 一边下载歌曲,一边听歌
import time
import threading
def down_load():
for i in range(1,6):
print("正在下载歌曲")
time.sleep(1)
def listen():
for i in range(1,3):
print("正在听歌")
time.sleep(1)
def main():
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 守护线程
# t1.setDaemon(True)
t2.setDaemon(True)
# 执行线程
t1.start()
t2.start()
if __name__ == '__main__':
main()
print("QQ音乐软件关闭了") # 主程序
6.threading模块提供的方法
- threading模块提供的方法
- threading.currentThread():返回当前的线程变量
- threading.enumerate():返回一个包含所有正在运行的线程list。正在运行的线程:启动后,结束前,不包括启动前和终止后的线程
- threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
- 线程.getName():获取线程名称
- 线程.setName():设置线程名称
- 线程.is_alive():判断线程存活状态
threading.currentThread():返回当前的线程变量
# 一边下载歌曲,一边听歌
import time
import threading
def down_load():
for i in range(1,6):
print("正在下载歌曲")
time.sleep(1)
def listen():
for i in range(1,3):
print("正在听歌")
time.sleep(1)
def main():
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 执行线程
t1.start()
t2.start()
# 查看当前线程
print("当前线程:",threading.current_thread())
if __name__ == '__main__':
main()
print("QQ音乐软件关闭了") # 主线程
# 查看当前线程
print("当前线程:",threading.current_thread())
# 一边下载歌曲,一边听歌
import time
import threading
def down_load():
for i in range(1,6):
print("正在下载歌曲")
time.sleep(1)
def listen():
for i in range(1,3):
print("正在听歌")
time.sleep(1)
def main():
# 创建线程
t1 = threading.Thread(target=down_load)
t2 = threading.Thread(target=listen)
# 执行线程
t1.start()
t2.start()
# 返回正在运行的线程列表。正在启动运行的线程:启动后,结束前,不包括启动前和终止后的线程
print("当前线程:",threading.enumerate())
if __name__ == '__main__':
main()
print("QQ音乐软件关闭了") # 主线程
# 查看当前线程
threading.activeCount() 返回存活总数
threading.getName()和setName 返回线程列表
is_alive()判断线程是否存活
7.线程共享全局变量
1、先看简单的线程全局变量变化
import threading
import time
def work1(g_nums):
g_nums.append(44)
print(f"--in work1 g_num is {g_nums}")
def work2(g_nums):
time.sleep(0.5)
print(f"--in work2 g_num is {g_nums}")
g_nums = [11,22,33]
t1 = threading.Thread(target=work1,args=(g_nums,))
t1.start()
t2 = threading.Thread(target=work2,args=(g_nums,))
t2.start()
可以看到调用全局变量的时候,仅仅在work1函数中,修改了添加了一个“44”变量,但是在work2中的线程调用全局变量的时候,全局变量数据一致
2、根据休眠顺序修改全局变量的数据
谁先修改全局注意观察
休眠的线程会让另外个没有休眠的线程执行获取全局变量
现在将线程work1休眠0.5秒
在0.5秒的休眠时候,足够让线程2先获得全局变量
可能发现还没有特别的改变,现在将线程wokr2,开启修改全局变量
我们对比一下,同样的work1 和 work2 也开启了修改,我们来看下谁先修改
发现线程2 修改完毕后,才轮到线程1进行修改,因为线程1,休眠了0.5秒
8.多线程开发可能会遇到的问题
"""
import threading
import time
g_num = 0
def work1(num):
global g_num
for i in range(num):
g_num +=1
print(f"-- in work1,g_num is {g_num}")
def work2(num):
global g_num
for i in range(num):
g_num +=1
print(f"-- in work2,g_num is {g_num}")
print("创建线程之前g_num:",g_num)
t1 = threading.Thread(target=work1,args=(100,)) # 100看不出来
t1.start()
t2 = threading.Thread(target=work2,args=(100,))
t2.start()
我们不是启动了两个线程吗?t1,t2,但是为甚么t2为什么并没有看到,周期太短的问题,传入参数的时候为100,并不能很明显的看出来。现在将参数调高一点,注意,每个人的电脑CPU反应不同,所以需要传入的参数不同,自行调试
线程在进行运行操作的时候,其实都有进行切换的,抢占CPU运算资源,我们作为人是观察不到计算机的运行,但是,实际存在在这种情况的,可能前一秒在t1 修改全局变量,下一秒就跑到t2,t1修改的时候如果本身数据为93
但是t2可能抢到了,可能刚开始起步加,数据为101,于是下一次t1 获取的全局变量又变成了101,双方都存在这种情况。那么可能t2在修改的时候修改到了152,但是t1修改才97,造成数据访问冲突,调用冲突,可能将全局变量数据又变成了97,t2又需要重新加
这就是线程抢占资源,线程出现的情况,那么如何避免这种情况呢?
join()线程资源独占!!!!!!!!!!严格意义上是单线程,串行
或者让某个线程休眠一下
返回文章目录
当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个 线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。
使用过程,应用场景:某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定状态,其他线程就能更改,直到该线程将 资源状态改为非锁定状态,也就是释放资源,其他的线程才能再次锁定资源。
互斥锁保证了 每一次只有一个线程进入写入操作。从而保证了多线程下数据的安全性
互斥锁为资源引入的一个状态:锁定/非锁定
import threading
# 创建锁
mutex = threading.Lock()
# 锁定,上锁
mutex.acquire()
# 释放,解锁
mutex.release()
#注意:
1.如果这个锁之前没有上锁,那么acquire不会堵塞
2.如果在调用acquire,这个锁上锁之前,他已经被其它线程上锁,此时acquire会堵塞,
直到锁被解锁为止
(一)生活中类比线程锁
(二)解决线程数据冲突问题
在上面的时候,知道了为什么会多线程访问修改全局数据,这是因为抢占资源的原因
import threading
import time
# 使用互斥锁完成2个线程对同一全局变量操作
g_num = 0
def work1(num):
global g_num
for i in range(num):
# 锁定,上锁
mutex.acquire()
g_num +=1
# 释放,解锁
mutex.release()
print(f"-- in work1,g_num is {g_num}")
def work2(num):
global g_num
for i in range(num):
# 锁定,上锁
mutex.acquire()
g_num +=1
# 释放,解锁
mutex.release()
print(f"-- in work2,g_num is {g_num}")
# 创建锁
mutex = threading.Lock()
t1 = threading.Thread(target=work1,args=(1000000,)) # 100看不出来 根据每个用户CPU不同调数值
t1.start()
t2 = threading.Thread(target=work2,args=(1000000,))
t2.start()
咦,结果怎么只有线程2,成功的加到了2百万了?上锁是将数据资源锁定,不会出现抢占资源,在前面的时候
返回文章目录
# 在多个线程共享资源的时候,如果两个线程分别占有一部分资源,
# 并且同时等待对方的资源, 就会造成死锁现象,尽管死锁很少发生,
# 但是一旦发生就会造成应用程序停止相应
import threading
# #创建锁
# mutex = threading.Lock()
#
# #锁定
# mutex.acquire()
#
# #释放
# mutex.release()
import time
def test1():
#lock1上锁
lock1.acquire()
print("--test1开始执行--")
time.sleep(1)
# lock2上锁
lock2.acquire()
print("--test1执行完成--")
lock2.release() #lock2解锁
lock1.release() #lock1解锁
def test2():
# lock2上锁
lock2.acquire()
print("--test2开始执行--")
time.sleep(1)
# lock1上锁
lock1.acquire()
print("--test2执行完成--")
lock1.release() # lock1解锁
lock2.release() # lock2解锁
lock1 = threading.Lock()
lock2 = threading.Lock()
if __name__ == '__main__':
t1 = threading.Thread(target=test1)
t2 = threading.Thread(target=test2)
t1.start()
t2.start()
队列:就是一个典型的线程安全的容器
from queue import Queue
一定要先导入模块
from queue import Queue
# 创建队列对象
q = Queue(3) # 队列存储3个数据
q2 = Queue(maxsuze=3) # 队列存储 3个数据
名称 | 解释 | 用法 |
---|---|---|
put() | 向队列存放数据依次排队 ,如果此方法阻塞,直到队列可用为止 | q.put(“数据1”) |
get(timeout) | 返回队列中头部的一个数据并且删除该数据,如果队列为空,阻塞,也可以设置等待时间,时间到了没有获得,抛出异常 | print(q.get()) |
get_nowait() | 不等待得到队首数据,没有则抛出异常 | print(q.get_nowait()) |
full() | 判断队列是否满了 满True 不满False | print(q.full()) |
empty() | 判断队列是否为空,bool 空True 非空False | print(q.empty()) |
qszie() | 队列中的数据个数 | print(q.qsize()) |
from queue import Queue
# 参数maxsize 是队列中云讯的最大项,如果省略参数,则无大小限制
# 返回值q 是队列对象
#
#
# put()方法,向队列存放数据,如果q为空,此方法阻塞,直到队列可用为止
# get()方法,返回队列中的一个数据,该数据被删除,如果队列为空,此方法阻塞
# get_nowait(),不等待,直接抛出异常
# full(),判断队列是否满了,如果队列对象已满,bool
# empty(),判断队列是否为空,bool 空True 非空False
# qszie(),队列中的数据个数
# 创建队列对象
q = Queue(maxsize=3) # 3 表示最多存放3个数据 q =
q.put("数据1")
print(q.empty())
q.put("数据2")
q.put("数据3")
print(q.full())
# q.put("数据4") # 程序阻塞
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get(timeout=3))# 等待3秒 取 没有数据抛出异常
# print(q.get_nowait())
print(q.qsize())
返回文章目录
(一)什么是生产者消费者
为什么使用生产者和消费者模式
什么是生产者消费者模式
该模式配合队列使用
(二)生产者消费者实例说明
import threading
import queue
import time
# 生产者:厨师
def producer(name):
count = 1
while True:
print("%s生产了包子%d" % (name, count))
q.put(count) # 将包子存放到容器
count +=1 # 循环一次 生产一次包子
time.sleep(0.2) # 厨师生产速度
print("包子总数",q.qsize())
# 消费者:顾客
def customer(name):
while True:
print("%s吃了包子%d"%(name,q.get()))
time.sleep(1)# 消费者吃包子的速度
if __name__ == '__main__':
# 创建队列容器
q = queue.Queue(3) # 容器只能装下3个包子
# 创建一个厨师A,两个顾客 B C,三个线程
t1 = threading.Thread(target=producer,args=("厨师A",))
t2 = threading.Thread(target=customer,args=("消费者A",))
t3 = threading.Thread(target=customer,args=("消费者B",))
# 启动线程
t1.start()
t2.start()
t3.start()
可以看到,厨师生产的速度刚好满足,消费者的消费速度
"""
秒杀系统:底层原理模拟
"""
import threading
import time
from queue import Queue
# 中间仓库
goodses = Queue(10)
def produce():
"""商家"""
while True:
try:
# 商家负责提供商品
time.sleep(1)
goodses.put_nowait("手机")
print("商品已经调度一台,存放到商店")
except Exception as e:
print("仓库已满,商家等待中....")
def consumer():
"""客户"""
while True:
try:
# 客户负责购买商品
time.sleep(0.5)
goods = goodses.get_nowait()
print(f"购买到一个商品:{goods}")
except Exception as e:
print("正在排队,商品正在调度中,请等待....")
if __name__ == "__main__":
# 商家
p1 = threading.Thread(target=produce)
p2 = threading.Thread(target=produce)
p3 = threading.Thread(target=produce)
p1.start()
p2.start()
# p3.start()
# 用户
c1 = threading.Thread(target=consumer)
c2 = threading.Thread(target=consumer)
c3 = threading.Thread(target=consumer)
c1.start()
c2.start()
c3.start()
1、使用函数传参的方法
def process_student(num):
std = Student(name)
do_task_1(std)
do_task_2(std)
def do_task_1(std):
do_task_3(std)
do_task_4(std)
def do_tasl_2(std):
do_task_5(std)
do_task_6(std)
每个函数一层一层调用都这么传参数那还得了?用全局变量?也不行,因为每个线程处理不 同的 Student 对象,不能共享
2、使用全局字典的方法
如果用一个全局 dict 存放所有的 Student 对象,然后以 thread 自身作为 key 获得线程对应的 Student 对象如何
import threading
global_dict = {
}
def std_thread(num):
std = Student(name)
# 把std放到全局变量global_dict中
gloabal_dict[threading.current_thread()] = std
do_task_1()
do_task_2()
def do_task_1():
# 不传入std 而是根据当前线程查找
std = global_dict[threading.current_thread()]
pass
def do_task_2():
# 任何函数都可以查找出当前线程的std变量
std = global_dict[threading.current_thread()]
这种方式理论上是可行的,它最大的优点是消除了 std 对象在每层函数中的传递问题,但是, 每个函数获取 std 的代码有点 low 有没有更简单的方式
3、切入正题使用threadLocal的方法
threadLocal 应运而生,不用查找 dict,threadLocal 帮你自动做这件事
import threading
user = threading.local() # 全局变量,但是每个线程调用的时候互不影响
def print_message():
user_name = user.name
print(user_name,threading.current_thread().name)
def write_message(message):
user.name = message # 自定义变量name假设是local的属性,给该属性赋值
print_message() # 调用打印信息
if __name__ == '__main__':
t1 = threading.Thread(target=write_message,args=("用户A",),name="Thread-A")
t2 = threading.Thread(target=write_message,args=("用户B",),name="Thread-B")
t1.start()
t2.start()
t1.join()
t2.join()
如图下图结果:
全局变量userl就是一个ThreadLocal对象,每个线程对它都可以读写name属性,但互不影响。可以把user看成全局变量,但每个属性如user.name都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题。
1、全局解释锁的定义
全局解释器锁[Global Interpreter Lock]是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。即便在多核处理器上,使用 GIL 的解释器也只允许同一时间执行一个线程,常见的使用 GIL 的解释器有CPython与Ruby MRI。可以看到GIL并不是Python独有的特性,是解释型语言处理多线程问题的一种机制而非语言特性。
详情参看全局解释锁文章作者:静海听风https://zhuanlan.zhihu.com/p/87307313
此处引用1和2
返回文章目录
import multiprocessing
基本语法:
def test():
自定义函数
# 创建一个独立的进程
p1 = multiprocessing.Process(target=这个进程中要执行的函数) # 启动进程:固定语法,调用 start()启动进程
p1.start()
"""
传统:多进程多任务
multi 多个
process 进程
multiprocessing: python中用于处理多进程的一个模块
"""
import time
import multiprocessing
def eat():
"""吃饭"""
for i in [1,2,3]:
print("正在吃饭....")
time.sleep(1)
def read():
"""阅读"""
for i in [1,2]:
print("看文章...")
time.sleep(1)
#主进程的判断
if __name__ == "__main__":
# 根据两个任务,创建两个进程
eat_p1 = multiprocessing.Process(target=eat)
read_p2 = multiprocessing.Process(target=read)
# 启动两个进程
eat_p1.start()
read_p2.start()
返回文章目录