‘’’
Python的进程:
任务管理器,详细信息:观察列表中的PID
Linux:Terminal下运行ps -a
ps -a
ps -all
PID:是操作系统中运行的进程编号, 进程号越小,表示进程越重要
进程的概念:
狭义上讲:特指运行起来的程序
广义上讲:进程是一个具有特定功能的程序关于某个数据集合的一次运行活动
程序和进程的区别:
①. 程序是指令和数据的有序集合,程序本身不运行没有任何意义,程序是
静态的。进程是程序在处理机制上的一次执行过程,进程是动态的。
②. 程序可以作为软件的资料而长期存在,进程具有生命周期。
进程生命周期:创建、等待调用、运行、阻塞、消亡
③. 程序是永久的,进程是暂时的。
进程的调度机制:
启动多个进程交替执行时,操作系统会采用一定的调度机制,
让程序依据特定执行方式运行。
①. FCFS,先来先服务调度机制,依照进程创建顺序依次执行。
适用于长期执行的进程。
②. SJ/PF,短作业优先执行调度机制,适用于短进程频繁执行。
③. 时间片轮转调度机制,让每个进程在等待队列中被分配相等的时间,
轮转执行。
并行与并发:
CPU的核数,N核,2N线程:
微观串行,宏观并行,与CPU主频相关
并行是指,资源足够时,任务同时执行。
串行是指,资源短缺时,任务依次执行。
并发是指,资源有限时,多任务交替执行。
进程的创建与进程的结束
操作系统创建进程的四种方式:
①.系统初始化过程,如:开启系统后执行的:中断、电源管理、USB驱动等等
②.一个进程执行过程中,开启另外一个进程。
关系依次为父进程与子进程(PPID,PID)
③.用户的交互请求时会创建进程,比如打开浏览器的图标
④.执行批处理的初始化过程
进程的结束
①.进程正常的退出
②.进程出错退出
③.进程导致系统错误退出
④.其他任务杀死进程: ctrl + c, kill -9
Python进程创建:
通过multiprocessing库中的Process模块实现进程的创建。
①. 进程的创建
②. 进程的同步
③. 进程池的操作
④. 进程之间的数据交互
Process模块的使用:
from multiprocessing import Process
Process.__init__(self, group=None, target=None, \
name=None, args=(), kwargs={})
参数介绍:
使用时,需要进行关键字传参
group:模块预留参数,不使用,值始终是None
target:表示调用的对象,就是子进程要执行的任务
name:子进程的名称
args:表示调用对象的位置参数元组, args=(1,2)
kwargs:表示低啊用对象的字典,kwargs={name='alex',value=100}
方法介绍:
P.start(): 启动进程 ,并且调用进程中的方法 P.run()
P.run(): 进程启动时,运行的方法,通过这个方法调用target指定的函数
P.terminate(): 强制终止进程P, 不进行清理。如果P创建了子进程,终止P
会导致P的子进程无法操作,子进程转变为僵尸进程
P.is_alive(): 如果P正在运行,返回布尔值True
P.join([timeout]):主进程等待子进程运行结束
属性介绍:
P.name: 进程名称
P.pid: 进程号
P.exitcode: 进程退出的返回值,正常退出时返回None
P.authkey: 进程的身份验证键,默认是os.urandom()生成的32位字符串,
可以为网络通信提供安全保障
P.daemon:返回布尔值,如果是后台进程返回True,否则返回False
创建子进程:
import time,os
from multiprocessing import Process
# Process.__init__(self, group=None, target=None, name=None, args=(), kwargs={})
# def func(name):
# print('Hello',name)
# print('子进程')
#
# if __name__=='__main__':
# print('主进程开始')
# P = Process(target=func, args=('multiprocessing',))
# # start()开启进程
# P.start()
# time.sleep(1)
# print('主进程结束')
# 运行多个子进程
# os.getpid(),获取本进程的进程号, os.getppid(),获取本进程的父进程的进程号
# def func():
# print('子进程id:',os.getpid(),'父进程id:', os.getppid())
#
# if __name__=='__main__':
# print('主进程id:',os.getpid())
# P_List=[]
# for i in range(5):
# P = Process(target=func)
# time.sleep(1)
# P.start()
# # P.join() #串行 运行子进程后再执行主进程代码
# P_List.append(P)
# for p in P_List:
# p.join() #并发机制实现,所有子进程执行结束再运行主进程代码
# print('主进程结束')
# 通过继承Process类,自定义进程类
# class ProcessClass(Process):
# # Process类本身也有__init__()方法,实例化这个类的过程中,
# # 也完成了与Process类实例化一样的操作过程, 同时,在这里
# # 传入延时时间参数用于重写run方法
# def __init__(self, interval):
# Process.__init__(self)
# self.interval = interval
# #重写run方法,调用P.start()方法过程中执行了这个方法
# def run(self):
# print('子进程ID %s 开始执行,父进程ID %s'%(os.getpid(),os.getppid()))
# time_start = time.time()
# time.sleep(self.interval)
# time_stop = time.time()
# print('子进程 %s 执行结束,耗时%.2f秒'%(os.getpid(), time_stop-time_start))
#
# # 主进程代码
# def main():
# t_start = time.time()
# #创建子进程
# P = ProcessClass(2)
# P.start()
# P.join(0.5)
# t_stop = time.time()
# print('主进程 %s 执行结束,耗时%.2f秒' % (os.getpid(), t_stop - t_start))
#
# if __name__ == '__main__':
# main()
守护进程:
进程分为前台进程和后台进程,守护进程是一种特殊的后台进程。
守护进程运行不受其他进程控制。 守护进程运行时,处于原地待命状态
有访问才执行,通常在系统休眠时才退出。
主进程 创建守护子进程:
①:守护进程会在主进程代码执行结束终止
②:守护进程内部无法开启新的子进程,否则抛出异常
AssertionError: daemonic processes are not allowed to have children
from multiprocessing import Process
import os, time
def task():
print('子进程%s 开始执行'%os.getpid())
time.sleep(2)
print('子进程%s 执行结束'%os.getpid())
#守护进程中 增加子进程会抛出异常
# P = Process(target=time.sleep, args=(3,))
# P.start()
def main():
print('主进程执行开始')
P = Process(target=task)
P.daemon = True
P.start()
P.join()# join可以调用后台的守护进程
print('主进程执行结束')
if __name__ == '__main__':
main()
守护子进程与非守护子进程并存:
from multiprocessing import Process
import time, os
def deamonFunc():
print(123)
time.sleep(1)
print('end123')
def Func():
print(456)
time.sleep(3)
print('456end')
def main():
P1 = Process(target=deamonFunc)
P2 = Process(target=Func)
P1.daemon = True
P1.start()
P2.start()
print('End!')
if __name__ == '__main__':
main()
# 随堂练习题:
# 创建多进程实现文件写入:mutiprocessing.txt,
# 对比常规方式执行时间:open.txt
# http://www.baidu.com
# http://www.taobao.com
# http://www.sina.com
# http://www.jd.com
# http://www.qq.com
# http://www.douban.com
# http://www.google.com
# http://www.163.com
‘’’
进程:
1. 进程号与父进程号
os.getpid()/os.getppid()
2. 并行、串行与并发
并行:资源足够,任务同时执行
串行:资源短缺,任务依次执行
并发:资源有限,多任务交替执行
3. mutiprocessing库,Process
Process.init()
target=进程对象
args=(,)进程对象的参数
P.start()
P.run()
P.join()
def SubFunc():
print('Hello world!')
def main():
PList=[]
for i in range(5):
P = Process(target=SubFunc)
PList.append(P)
P.start()
# P.join() #串行 执行子进程
# for i in PList:
# P.join() #并发执行
print('Done!')
main()
创建进程的方法:
1. 使用实例化Process对象,引用内部方法:start join
2. 继承方式实现子进程的创建
class MyProcess(Process):
def __init():
pass
def run():
#重写run方法
pass
任务:进行文件读写,创建多进程,与常规写入方式进行对比。
多进程效率>单进程执行
多进程:操作多个文件
条件:工作量相同情况下
影响运行结果的主要因素:磁盘文件处理能力, SSD,机械硬盘
‘’’
‘’’
新内容:
①. 进程的其他方法:P.terminate()
进程状态:正在运行状态,暂停,消亡
------------------------------------------
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,person):
super().__init__()
self.person = person
def run(self):
# 功能,输出人名,休眠2秒
print('%s 正在睡觉'%self.person)
time.sleep(2)
print('%s 还在睡觉'%self.person)
def mainFunc():
# 传参,Person
P1 = MyProcess('XXX')
P1.start()
P1.terminate() #关闭进程
for i in range(100):
print(P1.is_alive()) #判断进程是否存活
time.sleep(0.01)
# print(P1.is_alive()) # 判断进程是否存活
if __name__ == '__main__':
mainFunc()
------------------------------------------
②. 进程的同步:mutiprocessing.Lock
多进程执行时,进程直接没有顺序,为了方便对进行控制,
使进程执行完全。增加代码的安全性,加入了进程同步机制
------------------------------------------
from multiprocessing import Process,Lock
import time,os
def work(lock,n):
lock.acquire() #加锁,同步
print('%s :%s is running'%(n, os.getpid()))
time.sleep(2)
print('%s :%s is Done' % (n, os.getpid()))
lock.release() #解锁,同步结束
def main():
lock = Lock()
for i in range(3):
P = Process(target=work,args=(lock,i,))
P.start()
if __name__ == '__main__':
main()
------------------------------------------
③ 练习: 伪抢票程序:
# 手动创建一个文本 db.txt,写入票数:1
# 代码中实现以下功能:
# 主进程中创建20个子进程
# 子进程实现功能:
# 1.查票,显示剩余票数
# 2.买票,当前票数-1,显示剩余票数,以及买票成功信息
# 3. 如果剩余票数<=0,显示买票失败
from multiprocessing import Process,Lock
def search():
with open('./db','r') as f:
return f.read()
def get():
count = int(search())
print('剩余票数为%d'%count)
if count>0:
with open('./db','w') as f:
f.write(str(count-1))
print('买票成功,剩余票数%d'%(count-1))
return
print('买票失败,剩余票数%d'%count)
def task(lock):
search()
lock.acquire()
get()
lock.release()
def mainFunc():
lock=Lock()
for i in range(20):
P = Process(target=task, args=(lock,))
P.start()
if __name__ == '__main__':
mainFunc()
# Json读写
from multiprocessing import Process,Lock
import json, time
def search():
dic = json.load(open('db'))
print('剩余票数为%s'%dic['count'])
def get():
# 加载json格式数据
dic = json.load(open('db'))
time.sleep(2)
if dic['count']>0:
dic['count']-=1
#写入json格式内容到文件中
json.dump(dic, open('db','w'))
print('购票成功,剩余票数:%s' % dic['count'])
else:
print('购票失败,剩余票数:%s'%dic['count'])
def task(lock):
search()
lock.acquire()
get()
lock.release()
def mainFunc():
lock = Lock()
for i in range(10):
P = Process(target=task,args=(lock,))
P.start()
if __name__ == '__main__':
mainFunc()
④. 进程间通信:IPC (Inter Process Communication)
代表进程直接可以进行数据传递。
多进程实现的进程间通信,进程共享队列:
from multiprocessing import Queue
q = Queue([maxsize])
创建指定大小的队列
参数:maxsize是队列最大允许的项数
队列方法介绍:
get(self, block=True, timeout=None)
q:是队列中的一个项目,如果q为空这个方法将处于阻塞状态,
直到队列中有项目为止。
block用于控制阻塞行为。默认为True。如果设置为Fals,将会引发
异常Queue.empty
timeout:超时时间,用于在阻塞模式下,没有项目可用会引发异常:
Queue.empty
get_nowait(self) 方法 与q.get(False)含义相同
put(self, obj, block=True, timeout=None)
将数据对象执行一次入队,
block用于控制阻塞行为。默认为True。如果设置为Fals,将会引发
异常Queue.empty
timeout:超时时间,用于在阻塞模式下,没有项目可用会引发异常:
Queue.empty
put_nowait(self, obj):
empty(self): 显示队列状态,如果队列为空,返回True,否则返回False
full(self): 显示队列状态,如果队列已满,返回True,否则返回False
qsize(self): 返回队列中项目的数量,Mac系统下会出错
--------------------------------------------------
from multiprocessing import Queue
q = Queue(3)
q.put(1)
q.put(2)
q.put(3)
try:
q.put_nowait(4) # 队列已满,再次入队会导致报错,如果这里再次使用q.put会导致队列阻塞
except Exception:
print('队列已满')
print(q.full())
print(q.get())
print(q.get())
print(q.get())
try:
q.get_nowait() #如果继续使用get会导致队列阻塞
except Exception:
print('队列已空')
print(q.empty())
print(q.qsize())
---------------------------------------------
#在父进程中创建子进程, 子进程发送数据给父进程
from multiprocessing import Process, Queue
import time
def Sub(Q):
for i in range(1000):
Q.put([time.asctime(), 'from Sub','Hello'])
time.sleep(0.01)
def main():
Q = Queue()
P = Process(target=Sub,args=(Q,))
P.start()
for i in range(1000):
print(Q.get())
if __name__ == '__main__':
main()
# 子进程直接的数据交互
# 批量生产数据,放入队列再批量获取数据
from multiprocessing import *
import time, os
def inputFunc(Q):
info = str(os.getpid())+'put'+str(time.asctime())
Q.put(info)
def outpuFunc(Q):
info = Q.get()
print(info)
def mainFunc():
Q = Queue()
#创建生产数据的10个子进程
for i in range(10):
P = Process(target=inputFunc, args=(Q,))
P.start()
#创建获取数据的10个子进程
for i in range(10):
P = Process(target=outpuFunc, args=(Q,))
P.start()
if __name__ == '__main__':
mainFunc()
‘’’
#生产者与消费者
from multiprocessing import Process, Queue
import time, os
做包子
def producer(Q):
for i in range(10):
time.sleep(1)
Q.put(‘包子,%s’%i)
print(’%s 做包子 %s’%(os.getpid(),i))
Q.put(None)
吃包子
def consumer(Q):
while True:
info = Q.get()
if info == None:
break
print(’%s 吃包子 %s’%(os.getpid(), info))
def mainFunc():
Q = Queue()
P = Process(target=producer, args=(Q,))
C = Process(target=consumer, args=(Q,))
P.start()
C.start()
if name == ‘main’:
mainFunc()
主要内容:
1. IPC通信方式扩充:
队列Queue,数据交互方式是双向的
管道Pipe
使用Pipe()可以返回两个连接对象。这两个连接对象代表着管道的两端。
Pipe提供的方法:
P.send() 发送数据,
P.recv() 接收数据。
P.close()关闭管道
管道实现代码:
# 完成主进程中定义管道,定义俩子进程,
# 子进程1,负责发送数据,子进程2,负责接收数据
from multiprocessing import Pipe,Process,Queue,Lock
import os, time
def sender(conn):
# while True:
# info = str(os.getpid())+‘send running’
# Q.put(info)
# time.sleep(0.2)
while True:
info = str(os.getpid()) + ‘send running’
conn.send(info)
print(‘sender Function running, %s’%info)
time.sleep(0.2)
def recver(conn):
# while True:
# print(Q.get())
while True:
print(‘接收到数据’,conn.recv())
def mainFunc():
#创建子进程,让子进程进行通信
# Q = Queue()
# P1 = Process(target=sender,args=(Q,))
# P2 = Process(target=recver, args=(Q,))
# P1.start()
# P2.start()
lock = Lock()
send_conn,recv_conn = Pipe()
P1 = Process(target=sender,args=(send_conn,))
P2 = Process(target=recver, args=(recv_conn,))
lock.acquire()
P1.start()
P2.start()
lock.release()
if __name__ == '__main__':
mainFunc()
--------------------------------------------
进程池:
使用进程池来控制进程的数目,用于提高事物处理效率
Pool可以提供指定数目的进程,提供给用户调用,当有
新的请求提交到Pool时,如果池未满,那么会创建新的
进程来执行这条请求。 如果池中进程达到设定的最大值,
那么该请求就会等待,直到池中有进程结束。
进程池的创建流程:
①.导入进程池模块:
from multiprocessing import Pool
②.定义进程池,设定池子的大小
P = Pool(3)
③.设定进程调用方式:
阻塞式调用:后一个进程会等待前一个进程执行结束才开始执行。
P.apply()
非阻塞式调用:不会等待前一个进程执行结束
P.apply_async()
④.关闭进程池
P.close()
⑤.等待进程池执行完毕
P.join()
代码:
阻塞式同步调用:
from multiprocessing import Pool
import os,time
def work(n):
print('%s is running'%os.getpid())
time.sleep(1)
return n**2
def mainFunc():
P = Pool(6) #进程池中定义三个进程
countList = []
for i in range(20):
rec = P.apply(work,args=(i,))
# 同步调用,本次调用后,直接运行进程1,返回rec,work的返回值。
# 执行过程中,采用阻塞式执行进程,进程1任务执行结束后,如果处于空闲状态
# 进程1的PID会保留,等待下一次调用。如果阻塞时间过长,计算机可能夺走PID号
# 等到进程继续执行时,PID发生改变。
countList.append(rec)
print(countList)
if __name__ == '__main__':
mainFunc()
异步调用:
from multiprocessing import Pool
import os, time
def work(n):
print('%s is running' % os.getpid())
time.sleep(1)
return n ** 2
def mainFunc():
P = Pool(3) #进程池创建三个进程
countList = []
for i in range(20):
print(i) # for循环会提前运行print语句,进程池中任务还没有执行
recv = P.apply_async(work, args=(i,)) #同时,为进程池中进程增加任务,
# 并且获取 work函数的返回值。
countList.append(recv) #调用 apply_asyn方法时,返回的是 进程的内存地址结果
# 异步apply_async用法:如果使用异步方式提交任务,主进程需要使用join等待进程
# 内部任务处理完毕,然后才可以使用get方法采集结果。
# 否则,主进程结束,进程池可能没来得及执行,就一起结束了
P.close()
P.join()
for i in countList:
print(i.get(),end=' ') #使用get方法来获取apply_async的结果,
# 如果是apply没有get方法。原因是apply是同步执行的,立即返回结果,无需get
if __name__ == '__main__':
mainFunc()
#随堂练习:进程池实现创建多个子进程,抢票,db.txt,同步方式完成,默认票数3张,
进程池10个进程
from multiprocessing import Pool
import time,os
def SubFunc():
print(os.getpid())
with open('db','r') as f:
content = int(f.read())
if content>0:
with open('db','w') as f:
content-=1
f.write(str(content))
print('购票成功,剩余票数%d'%content)
else:
print('购票失败,剩余票数%d' % content)
time.sleep(0.5)
return content
def MainFunc():
recList = []
P = Pool(3)
for i in range(20):
rec = P.apply_async(SubFunc)
recList.append(rec)
P.close()
P.join()
# for i in recList:
# print(i.get())
if __name__ == '__main__':
MainFunc()
# 进程池方法:map(),可以将第二个参数以迭代访问的形式传到第一个函数类型的参数中
from multiprocessing import Pool
def Sub(i):
return i**2
def mainFunc():
P = Pool(processes=2)
H = P.map(Sub, [1,2,3])
print(H)
if __name__ == '__main__':
mainFunc()
2. 线程: Thread
a. 进程是计算机分配资源的最小单位,线程是CPU调度的最小单位。
CPU流水线。进程是一个实体,每一个进程都具备自己的内存空间,
内存空间通常包括:
文本区域(text region):
文本区域存储处理器执行的文本信息以及代码
数据区域(data region):
存储变量和执行进程期间使用的动态分配内存
堆栈区域(stack region):
存储活动过程中调用的指令以及本地的变量
b.进程只是把资源集中在一起(或者理解为是一种资源的集合),
线程是CPU上真正执行的单位。
c.一个进程中存在多个线程,进程中的多个线程共享进程的地址
空间以及进程资源
创建开销:
创建进程的开销,需要向操作系统申请一块内存空间并且放置资源进去
创建线程的开销,无需向操作系统申请空间,直接使用进程资源,效率高。
创建线程的方式:
mutiprocessing模块参照threading模块编写的,线程的创建与调用方式
与进程相似。
# 创建线程的第一种方式
from threading import Thread
import time,os
def Sub(name):
print('%s is running, PID %s'%(name,os.getpid()))
time.sleep(2)
print('%s is Stop, PID %s' %(name,os.getpid()))
def mainFunc():
print('Main thread is running, PID %s'%os.getpid())
T = Thread(target=Sub, args=('Sub Thread',))
T.start()
# T.join()
print('Main thread is Stop, PID %s' % os.getpid())
if __name__ == '__main__':
mainFunc()
--------------------------------------------------
#创建线程的第二种方式:继承
from threading import Thread
import time, os
class Sub(Thread):
def __init__(self, name, id):
Thread.__init__(self) #继承父类构造方法
self.name=name #增加属性,用于重写run方法
self.id = id
def run(self):
print('%s is running, PID %s' %(self.name, self.id))
time.sleep(1)
print('%s is Stop, PID %s' % (self.name, self.id))
def mainFunc():
print('Main thread is running, PID %s' % os.getpid())
S = Sub("Sub Thread",os.getpid())
# 在run方法中写入 name,pid,实现调用S的属性start开启线程,线程中输出name和pid信息
S.start() #创建并且执行子线程
# S.join() #join等待子线程执行结束后,在运行主线程其余代码
print('Main thread is Stop, PID %s' % os.getpid())
if __name__ == '__main__':
mainFunc()
# 在主进程下创建多个子线程
from multiprocessing import Process
from threading import Thread
import os
def work(name):
print('%s running,PID %s'%(name, os.getpid()))
def mainFunc():
#1.创建多个子进程
P1 = Process(target=work,args=('P1',))
P2 = Process(target=work,args=('P2',))
P1.start()
P2.start()
print('main Process PID,',os.getpid())
T1 = Thread(target=work,args=('T1',))
T2 = Thread(target=work,args=('T2',))
T1.start()
T2.start()
print('main Thread PID,',os.getpid())
if __name__ == '__main__':
mainFunc()
------------------------------------------------
Thread实例对象的方法:
is_alive(): 返回线程是否存活
isAlive = is_alive
getName(): 返回线程名
setName(): 设置线程名
threading模块提供的方法:
threading.current_thread():返回当前线程变量
threading.enumerate():返回一个包含正在运行线程的list。
正在运行的线程,启动后,终止前
activeCount = active_count
threading.active_count():
返回正在运行线程的数量。
-------------------------------------------
例子:
from threading import Thread
import threading,os,time
def work():
time.sleep(2)
print(threading.current_thread().getName())
def mainFunc():
# 在主进程中开启线程
T = Thread(target=work)
T.start()
T.join()
print(threading.current_thread().getName(), os.getpid())
print(threading.current_thread()) #主线程
print(threading.enumerate()) #连同主线程在内有多少个线程在执行
print(threading.active_count())
print('End!')
if __name__ == '__main__':
mainFunc()
--------------------------------------------------
守护线程:
线程也存在守护线程,它会等待主线程执行结束进行销毁
等待过程中都处于待命状态,通过一定调用方式可以运行守护线程
代码:
# 在下面代码中,主线程里再次创建子线程(非守护),同时运行守护线程与非守护线程
# 观察运行结果!
from threading import Thread
import time
def Daemon():
time.sleep(2)
print('Daemon is running')
def SubFunc():
time.sleep(2)
print('SubFunc is running')
def MainFunc():
T = Thread(target=Daemon)
T2 = Thread(target=SubFunc)
# T.daemon = True #守护线程方式一
T.setDaemon(True) #守护线程方式二
T.start()
T2.start()
# T.join()
print('Main Thread running')
print(T.isAlive)
if __name__ == '__main__':
MainFunc()
结论:Daemon是守护线程,SubFunc是非守护线程,与主线程共用一块内存
因此同时执行守护、非守护两个线程,Daemon会被执行。
‘’’