进程锁
# 当数据发生变化时,当采用的时多进程存在时间差,就会造成数据紊乱。
# 比如买票,假如这个时候只有一张票,但是所有人都能买,最后打印发现好几个人买到票了,
# 几个人没买到票。
# 因为在一个进程买票后,去修改票数时,由于时间太快,没来得及修改,就回到是其他进程买到票。
# 所以,这个时候就需要进程锁,
# 好比一个上锁的房间,门口只有一个钥匙,一个线程拿到钥匙进去修改数据没出来前,其他进程只能等着
import json
import multiprocessing
def show(i):
with open('ticket') as f:
dic = json.load(f)
print('余票:%s'%dic['ticket'])
def buy_ticket(i, lock): # 接收锁
lock.acquire() # 拿钥匙进门
with open('ticket') as f:
dic = json.load(f)
time.sleep(0.1)
if dic['ticket'] > 0:
dic['ticket'] -= 1
print('%s号买到票了'%i)
else:
print('%s号没买到票'%i)
time.sleep(0.1)
with open('ticket', 'w') as f:
json.dump(dic, f)
lock.release() # 还钥匙
if __name__ == '__main__':
lock = multiprocessing.Lock() # 实例化
for i in range(10):
p = multiprocessing.Process(target=buy_ticket, args=(i,lock)) # 传入锁
p.start()
进程池
在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,
并行操作可以节约大量时间,如果操作的对象数目不大时,还可以直接适用Process类动态
生成多个进程,几十个尚可,若上百个甚至更多时,手动限制进程数量就显得特别繁琐,
此时进程池就显得尤为重要。
进程池Pool类可以提供指定数量的进程供用户调用,当有新的请求提交至Pool中时,
若进程池尚未满,就会创建一个新的进程来执行请求;若进程池中的进程数已经达到
规定的最大数量,则该请求就会等待,直到进程池中有进程结束,才会创建新的进程来
处理该请求。
第一种实现进程池的方式:自带函数
# 第一种实现进程池的方式:自带函数
import multiprocessing
def job(id):
print("start %d...." % (id))
print("end %d...." % (id))
if __name__ == '__main__':
# 创建进程池对象
pool = multiprocessing.Pool(processes=4)
# 给进程池分配任务;
for i in range(10):
pool.apply_async(job, args=(i + 1,))
pool.close()
# 等待所有的子进程执行结束, 关闭进程池对象;
pool.join()
print("所有任务执行结束.....")
第二种实现进程池的方式:concurrent.futures模块
from concurrent.futures import ProcessPoolExecutor
def job(id):
print("start %d...." % (id))
print("end %d...." % (id))
pool = ProcessPoolExecutor(max_workers=4)
#
# for id in range(10):
# # 分配任务给子进程, 并且返回一个Future对象;
# f1 = pool.submit(job, args=(id))
# # 判断子进程是否执行结束?
# print(f1.done())
# # 查看该子进程执行的结果
# print(f1.result())
pool.map(job, range(10))
进程之间的通信:生产者与消费者模型
通信集中方式:
线程通信=====(队列) ---- from queue import Queue
进程池中进程通信=====(队列) — from multiprocess.Manager import Queue
多进程通信=========(队列) ---- from multiprocess import Queue
import multiprocessing
import time
class Producer(multiprocessing.Process):
def __init__(self,queue):
super(Producer, self).__init__()
self.queue=queue
def run(self):
# 将需要通信的数据写入队列中;
for i in range(10):
self.queue.put(i)
# time.sleep(0.1)
print("传递消息, 内容为%s" %(i))
class Consumer(multiprocessing.Process):
def __init__(self,queue):
super(Consumer, self).__init__()
self.queue=queue
def run(self):
while True:
time.sleep(0.1)
res=self.queue.get()
print("接受到另一进程传递的数据: %s" %(res))
if __name__ == '__main__':
q = multiprocessing.Queue()
p1 = Producer(q)
c1 = Consumer(q)
p1.start()
c1.start()
p1.join()
c1.join()
实例演示:实现文件的备份
import os
import time
import multiprocessing
from queue import Queue
def copyfile(oldFoldername,newFlodername,filename,queue):
# 打开选择的目录里边的文件
fr=open(os.path.join(oldFoldername,filename),'rb')
fw=open(os.path.join(newFlodername,filename),'wb')
with fr,fw:
# 设置一个一次最大读取的数据大小
try:
content=fr.read(1024*3)
# 每读取一次数据,就写入
if content:
fw.write(content)
print('write %s' %(filename))
except Exception as e:
print('error:',e)
else:
queue.put(filename)
def main():
while True:
oldFoldername=input('请输入备份的目录名:')
if os.path.exists(oldFoldername):
break
else:
print("%s目录不存在" % (oldFoldername))
datetime=time.strftime('%Y-%m-%d-%H-%M')
newFoldername=oldFoldername+'_备份'+datetime
if os.path.exists(newFoldername):
# os.rmdir(newFolderName) # 删除空目录
# os.removedirs(newFolderName)
os.system('rm -fr %s' %(newFoldername))
os.mkdir(newFoldername)
print('正在创建备份目录%s...'%(newFoldername))
# 获取当前目录下所有文件名
filenames=os.listdir(oldFoldername)
# 队列, 存储已经备份的文件;
# ****如果是用进程池,那么就需要使用Manager().Queue()队列才能在各子进程间通信,否则沒用
queue=multiprocessing.Manager().Queue()
# 设置进程池里边最大进程数
pool=multiprocessing.Pool(4)
for filename in filenames:
pool.apply_async(copyfile,args=(oldFoldername,newFoldername,filename,queue))
num=0 # 当前备份的文件数
allNum=len(filenames) # 总备份的文件数
while num<allNum:
queue.get()
num+=1
copyrate=num/allNum
# \r使得光标不换行;
print('\r\r备份的进度为%.2f%%'%(copyrate*100),end='')
pool.close()
pool.join()
print('备份成功')
if __name__ == '__main__':
main()
运行结果显示:
请输入备份的目录名:/home/kiosk/桌面/test
正在创建备份目录/home/kiosk/桌面/test_备份2019-08-28-17-41...
write file1
备份的进度为25.00%write file3
备份的进度为50.00%write file4
备份的进度为75.00%write file2
备份的进度为100.00%备份成功
进程之间的通信—管道
import multiprocessing
import time
def after(conn):
while True:
print('接收数据:',conn.recv())
time.sleep(1)
def before(conn):
while True:
data={'name':'westos','age':12}
# data=[12,34,55]
conn.send(data)
print('发送数据:',data)
time.sleep(1)
def main():
before_conn,after_conn=multiprocessing.Pipe()
p1=multiprocessing.Process(target=before,args=(after_conn,))
p2=multiprocessing.Process(target=after,args=(before_conn,))
p1.start()
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
main()
#---->:
发送数据: {'name': 'westos', 'age': 12}
接收数据: {'name': 'westos', 'age': 12}
发送数据: {'name': 'westos', 'age': 12}
接收数据: {'name': 'westos', 'age': 12}