文章目录
进程
多任务
操作系统上运行多个任务同时运行
并行:
任务的数量小于cpu的核心数量【理想型】
并发:
任务的数量大于cpu的核心数量。【现实】
单核cpu运行
操作系统处理方式:每个任务轮流交替执行,由于不同的任务之间的切换速度很快,导致肉眼无法识别
进程
一个任务即一个进程【Process】。
exp:打开一个浏览器。打开一个word启动一个word进程。但是,一个进程可以干很多事情(读、写等等),在一个进程的内部,要同时干很多件事情,就需要同时执行多个子任务,将一个进程中的多个子任务被称为线程【Thread】
进程的特点
a、独立性:不同的进程之间是相互独立的,在操作系统中互不影响,相互之间资源不共享
b、动态性:进程一旦被启动之后,在操作系统中并不是静止不动的,一直处于动态状态
c、并发性:在操作系统交替执行
多任务的实现方式:
多任务的实现方式
a、多进程模式:启动多个进程,每个进程虽然只有一个线程,通过改线程执行一个任务
b、多线程模式:启动一个进程,改改进程启动多个线程,每个线程执行一个任务
c、协程模式:yield【函数生成器】
d、多进程模式+多线程模式:启动多个进程,每个进程启动多个线程,执行的任务的数量会更多
multiprocessing模块(多进程模块)
Process
通过Process创建对象实现多进程
import time
import os
from multiprocessing import Process
def son():
# os,getpid 显示当前进程 系统编号
print("子进程开始了:%s\n" % os.getpid())
time.sleep(1)
print("子进程干活了\n")
time.sleep(1)
print("子进程结束了:%s\n" % os.getpid())
time.sleep(2)
if __name__ == '__main__':
print("主进程开始:%s\n" % os.getpid())
# 创建子进程对象
p = Process(target=son)
# 启动子进程
p.start()
# p.join() # 可以修改进程执行顺序
print("主进程结束了")
"""
主进程开始:7504
主进程结束了
子进程开始了:9692
子进程干活了
子进程结束了:9692
"""
"""
从结果上看出,执行的main和son()的是两个进程,
如果没有创建进程,直接执行son则线程号是一样的,
从上而下依次执行
p.join():修改执行顺序,父进程等待子进程结束后在继续执行
如果son有参数,则创建的时候也是需要以元组的形式传入。可以参考下面的进程池。
"""
Pool
pool:进程池 如果我们需要开启多个进程,可以通过Process一个个的创建,只是这样有点麻烦,Python内置了Pool这个类(object),用来创建多个进程,简化代码。
!注意:使用pool的时候先查看自己电脑是几核的(cpu内核数量,不懂自行百度),可以开启几个进程。
import time
import os
from multiprocessing import Pool
def run(mag):
# for _ in range(5):
print("Nice day~~~进程号:%s" % os.getpid())
time.sleep(1)
if __name__ == '__main__':
print("主进程开始~~~%s" % os.getpid())
time.sleep(1)
p = Pool(4) # 开启 4 个进程
for i in range(8): # 任务执行8次,
p.apply_async(run, args=(i,)) # 执行任务
p.close()
p.join()
print("主进程结束~~~%s" % os.getpid())
"""
p = pool(4) 这里表示我们允许(需要执行的函数)最多占用4个进程
p.apply_async(run,args=(i,)) 调用Pool的方法,参数为一个函数,即run
args=(i,)是run函数需要的参数,必须以元组的形式传入。
p.close()表示待循环完成以后关闭进程池
p.join() 表示子进程要执行,类似于Process的start(),开启进程池并且改变顺序。
等待子进程全部执行完毕,主进程继续。
"""
"""
主进程开始~~~10404
Nice day~~~进程号:9180
Nice day~~~进程号:11740
Nice day~~~进程号:7112
Nice day~~~进程号:4508
Nice day~~~进程号:11740
Nice day~~~进程号:9180
Nice day~~~进程号:7112
Nice day~~~进程号:4508
主进程结束~~~10404
"""
封装进程对象
import time
import os
from multiprocessing import Pool, Process
class Defined(Process):
def run(self) -> None: # "->" 在python中叫“注解”,自行百度
print("子进程开始:%s" % os.getpid())
time.sleep(2)
print("子进程结束:%s" % os.getpid())
if __name__ == '__main__':
print("主进程启动:%s" % os.getpid())
p = Defined()
p.start()
p.join()
print("结束%s" % os.getpid())
"""
主进程启动:10780
子进程开始:11096
子进程结束:11096
结束10780
"""
进程间的通信
Process之间是需要相互通信的,操作系统提供了很多的办法实现进程间的通信,Python中的multiprocessing包装了底层的机制,提供了Queue【队列】,Pipes等多种方式实现交换数据
#需求:在主进程中创建两个子进程,一个进程用来向队列中写数据,另外一个进程用来从队列中读数据
from multiprocessing import Process,Queue
import os,time,random
#1.写数据的任务
def write(queue):
print("进程%s开始" % (os.getpid()))
print("开始写入")
for value in ["A","B","C"]:
print("add %s to queue" % (value))
#向队列中添加数据
queue.put(value)
#稍休息片刻,接着加
time.sleep(random.random())
print("进程%s结束" % (os.getpid()))
#2.读数据的任务
def read(queue):
print("进程%s开始" % (os.getpid()))
print("开始读取")
n = 0
while n < 3:
#从队列中获取数据
value = queue.get(True)
print("get %s from queue" % (value))
n += 1
print("进程%s结束" % (os.getpid()))
#3.在主进程中分别创建子进程,执行相应的任务
if __name__ == "__main__":
print("父进程启动")
#3.1创建队列对象,并传参给每个进程
q = Queue()
#3.2创建进程对象,
pr = Process(target=read,args=(q,))
pw = Process(target=write,args=(q,))
#3.3启动子进程,让进行数据的读写
pw.start()
pr.start()
#3.4设置,让所有子进程结束之后,才结束父进程
pr.join()
pw.join()
print("父进程结束")
线程
of:是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务
注意:一个进程至少有一个子任务,否则改进程没有意义
线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是在cpu之间进行的快速的切换【每个线程都有争抢时间片的机会,谁抢到时间片,则执行对应的线程,剩下的线程都会被挂起】
比如: 打开网易云----》 启动了一个进程
播放歌曲和刷新歌词-----启动了两个线程
【面试题】进程和线程的关系
a、一个程序启动以后至少有一个进程
b、一个进程可以包含多个线程,但是至少需要一个线程,否则改进程没有意义
c、一个进程可以包含多个线程,线程之间是可以资源共享的
d、系统创建进程需要为该进程重新分配资源,而创建线程容易的多,因此使用多线程实现多任务比多进程实现效率更高。
创建线程
_thread模块:提供了低级别的,原始的线程【功能有限,底层采用的是c语言】
threading模块:高级模块,对_thread进行了封装,有_thread没有的功能
创建多线程
import time
import threading
def run():
# for _ in range(5):
print("Nice_day")
time.sleep(1)
if __name__ == '__main__':
se = threading.BoundedSemaphore(5) # 有没有都一样,限制最大线程数为5
t_list = []
for i in range(5):
t = threading.Thread(target=run)
t_list.append(t) # 将创建的线程保存在t_list中
for j in t_list:
j.start()
# run()
print("主线程结束")
"""
代码解释:
单线程执行是一个执行完再接着执行下一个,如run()注释掉的for语句,一次接着一次打印。
如果我们同时创建多个线程,则一次性执行5次run()。
"""
线程锁
原因
线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是在cpu之间进行的快速的切换【每个线程都有争抢时间片的机会,谁抢到时间片,则执行对应的线程,剩下的线程都会被挂起】
from threading import Thread,Lock
balance = 0
#创建一个锁对象
lock = Lock()
def change(n):
global balance
balance = balance + n
balance = balance - n
#子线程的任务
def run(n):
for _ in range(1000000):
#获取锁
lock.acquire()
try:
# 临界资源
change(n)
finally:
#释放锁
lock.release()
if __name__ == "__main__":
t1 = Thread(target=run,args=(5,))
t2 = Thread(target=run,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
#理论上,balance最终的结果为0
print(balance)
from threading import Thread,Lock
balance = 0
#创建一个锁对象
lock = Lock()
def change(n):
global balance
balance = balance + n
balance = balance - n
#子线程的任务
def run(n):
for _ in range(1000000):
#同样表示获取锁,但是,不用手动释放,当临界资源的代码执行完毕之后,锁会被自动释放
with lock:
# 临界资源
change(n)
if __name__ == "__main__":
t1 = Thread(target=run,args=(5,))
t2 = Thread(target=run,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
#理论上,balance最终的结果为0
print(balance)