进程
进程就是一个程序在一个数据集上的一次动态执行过程。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
linux下创建子进程
1). Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,
调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(
称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
2). 子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork
出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()
就可以拿到父进程的ID。
3). Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松
创建子进程:
原理:
父进程和子进程:, 如果父进程结束, 子进程也随之结束;
先有父进程, 再有子进程. 类Linux系统中(redhat,mac), fork函数;
常用函数:
os.fork()
os.getpid() # 获取当前进程的pid (process id)
os.getppid() # 获取当前进程的父进程pid (parent process id)
“”"
import os
print("当前进程(pid=%d)正在运行......." %(os.getpid()))
#在pycharm编写代码, 程序的父进程就是pycharm;
print("当前进程的父进程为(pid=%d)正在运行....." %(os.getppid()))
print("开始创建子进程.....")
pid = os.fork()
if pid == 0:
print("这是子进程返回的是0, 子进程的pid为%d, 父进程为%d" %(os.getpid(), os.getppid()))
else:
print("这是父进程返回的,返回值为子进程的pid, 为%d" %(pid))
可以在系统进程下找到父进程charm
进程池实现备份文件
import os
import time
import multiprocessing
from queue import Queue
def copyFileTask(oldFolderName,newFolderName,filename,queue):
fr = open(os.path.join(oldFolderName,filename),'rb')
fw = open(os.path.join(newFolderName,filename),'wb')
with fr,fw:
content = fr.read(1024)
while content:
fw.write(content)
# print('write %s' %(filename))
queue.put(filename)
def main():
oldFolderName = input('请输入备份的目录名:')
dateName = time.strftime('%Y_%m_%d_%H_%M')
newFolderName = oldFolderName+'_备份'+dateName
os.mkdir(newFolderName)
print('正在创建备份目录%s...'%(newFolderName))
fileNames = os.listdir(oldFolderName)
queue = multiprocessing.Manager().Queue()
pool = multiprocessing.Pool(2)
for name in fileNames:
# print("test", name)
pool.apply_async(copyFileTask,args=(oldFolderName,
newFolderName,
name,
queue))
num = 0
allNum = len(fileNames)
while num < allNum:
queue.get()
num+=1
copyRate = num/allNum
print('\r\r备份的进度为%.2f%%'%(copyRate*100),end ='')
pool.close()
pool.join()
print('备份成功')
if __name__ == '__main__':
main()
在/tmp目录下建立一个新的目录用来做测试
1.理解:
如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows
没有fork调用,难道在Windows上无法用Python编写多进程的程序?
由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing
模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了
启动一个子进程并等待其结束:
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
2.Process使用属性及方法
Process 类用来描述一个进程对象。创建子进程的时候,只需要传入一个执行函数和函数的参数即可完成 Process 示例的创建。
star() 方法启动进程,
join() 方法实现进程间的同步,等待所有进程退出。
close() 用来阻止多余的进程涌入进程池 Pool 造成进程阻塞。
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
target 是函数名字,需要调用的函数
args 函数需要的参数,以 tuple 的形式传入
“”"
import multiprocessing
def job():
print("当前子进程的名称%s....." %(multiprocessing.current_process()))
#通过类的实例化实现
p1 = multiprocessing.Process(target=job, name="我的第一个子进程")
p1.start()
#通过类的实例化实现
p2 = multiprocessing.Process(target=job, name="我的第2个子进程")
p2.start()
#join方法, 等待所有的子进程执行结束, 再执行主进程
p1.join()
p2.join()
print("任务执行结束.....")
类的继承实现多进程
import multiprocessing
class MyProcess(multiprocessing.Process):
# 重写run方法=====start方法默认执行run方法
def run(self):
print("当前子进程的名称%s....." % (multiprocessing.current_process()))
p1 = MyProcess(name="first")
p1.start()
p2 = MyProcess(name="second")
p2.start()
p1.join()
p2.join()
print("all finish.....")
多进程效率演示
import threading
import time
from mytimeit import timeit
import multiprocessing
def job(li):
return sum(li)
@timeit
def use_thread():
li = range(1, 100000000)
# create 5 threads
threads = []
for i in range(5):
t = threading.Thread(target=job, args=(li, ))
t.start()
threads.append(t)
[thread.join() for thread in threads]
@timeit
def use_no_thread():
li = range(1, 100000000)
for i in range(5):
job(li)
@timeit
def use_process():
li = range(1, 100000000)
# create 5 threads
processes = []
# 1). 开启的进程书是有瓶颈的, 取决于CPU个数,
# 2). 如果处理的数据比较小, 不建议使用多进程,因为创建进程和销毁进程需要时间;
# 3). 如果处理数据足够大, 0《进程数《cpu个数;
for i in range(5):
p = multiprocessing.Process(target=job, args=(li,))
p.start()
processes.append(p)
[process.join() for process in processes]
if __name__ == "__main__":
use_thread()
use_process()
use_no_thread()
多处理的数据量较大时,多进程的效率就显而易见了
进程锁
import multiprocessing
def work(f, item, lock):
# lock.acquire()
try:
with open(f, 'a+') as f:
f.write("a %s task\n" % (item))
except Exception as e:
print("产生异常...")
# finally:
# lock.release()
def main():
# 1). 实例化一个进程锁
lock = multiprocessing.Lock()
filename = 'doc/my.log'
processes = []
for i in range(4):
p1 = multiprocessing.Process(target=work, args=(filename, i,lock))
p1.start()
processes.append(p1)
[process.join() for process in processes]
if __name__ == '__main__':
main()
运行之后在my.log文件中
进程池
方法一
import multiprocessing
def job(id):
print("start %d...." % (id))
print("end %d...." % (id))
#创建进程池对象
pool = multiprocessing.Pool(processes=4)
#给进程池分配任务;
for i in range(10):
pool.apply_async(job, args=(i + 1,))
pool.close()
#等待所有的子进程执行结束, 关闭进程池对象;
pool.join()
print("所有任务执行结束.....")
方法二
from concurrent.futures import ProcessPoolExecutor
def job(id):
print("start %d...." % (id))
print("end %d...." % (id))
pool = ProcessPoolExecutor(max_workers=4)
pool.map(job, range(10))
多进程拷贝文件
只需对前面拷贝文件的代码稍加修改即可
import os
import time
import multiprocessing # 进行进程间的通信, Queue
from queue import Queue
def copyFileTask(oldFolderName, newFolderName, filename, queue):
"""
import os
# 拼接生成绝对路径
os.path.join('/mnt', 'file')
'/mnt/file'
os.path.join('/mnt/', 'file')
'/mnt/file'
:param oldFolderName: /root/day21/
:param newFolderName: /root/day21_backup_201901
:param filename: file1
:return:
"""
# 两者相同的效果, with语句执行节航速后, 自动关闭文件对象;
# with open('/etc/passwd') as f:
# pass
# f = open('/etc/passwd')
# with f:
# pass
fr = open(os.path.join(oldFolderName, filename), 'rb')
fw = open(os.path.join(newFolderName, filename), 'wb')
with fr, fw:
content = fr.read(1024*3)
while content:
fw.write(content)
queue.put(filename)
# print(queue.qsize())
def main():
# 判断备份目录是否存在
while True:
# oldFolderName = input("请输入备份的目录名:")
oldFolderName ="/var/log/"
if os.path.exists(oldFolderName):
break
dateName = time.strftime('_%Y_%m_%d_%H_%M') # '2019_01_20'
newFolderName = oldFolderName + '_备份' + dateName
if os.path.exists(newFolderName):
os.rmdir(newFolderName)
# 新建备份的目录;
os.mkdir(newFolderName)
print("正在创建备份目录%s....." % (newFolderName))
# 获取备份目录中的所有文件名;
fileNames = os.listdir(oldFolderName)
# 队列, 存储已经备份的文件;
# ****如果是用进程池,那么就需要使用Manager().Queue()队列才能在各子进程间通信,否则沒用
queue = multiprocessing.Manager().Queue()
# queue = Queue()
pool = multiprocessing.Pool(4)
for name in fileNames:
# 给进程池分配任务
pool.apply_async(copyFileTask, args=(oldFolderName,
newFolderName,
name,
queue))
# 100个文件, 1个文件 1%
num = 0 # 当前备份的文件数
allNum = len(fileNames) # 总备份的文件数
# print(num, allNum)
while num < allNum:
# print(queue.qsize())
queue.get()
num += 1
copyRate = num / allNum # 0.2322
# \r使得光标不换行;
print("\r\r备份的进度为%.2f%%" % (copyRate * 100), end='')
pool.close()
pool.join()
print("备份成功;")
if __name__ == '__main__':
main()
进程间的通信之生产者消费者模型
import multiprocessing
#线程通信=====(队列) ---- from queue import Queue
#进程池中进程通信=====(队列) --- from multiprocess.Manager import Queue
#多进程通信=========(队列) ---- from multiprocess import Queue
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)
recvData = self.queue.get()
print("接受到另一进程传递的数据: %s" %(recvData))
if __name__ == '__main__':
q = multiprocessing.Queue()
p1 = Producer(q)
c1 = Consumer(q)
p1.start()
c1.start()
p1.join()
c1.join()
进程间的通信–管道
#1). Pipe管道,进程间通信的方式, l类似于 ls | wc -l;
#2). Pipe()返回两个连接对象, 分别代表管道的两边;
#3). 管道通信操作的方法: send(), recv;
#4). 管道间的通信是双向的, 既可以发送,也可以接收;
“”"
import multiprocessing
#线程通信=====(队列) ---- from queue import Queue
#进程池中进程通信=====(队列) --- from multiprocess.Manager import Queue
#多进程通信=========(队列) ---- from multiprocess import Queue
import time
def after(conn):
while True:
print("接收到数据:", conn.recv())
time.sleep(1)
def before(conn):
while True:
data = [42, None, 34, 'hello']
conn.send(data)
print("正在发送数据:%s" % (data))
time.sleep(1)
def main():
# send recv
before_conn, after_conn = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=after, args=(after_conn,))
p1.start()
p2 = multiprocessing.Process(target=before, args=(before_conn,))
p2.start()
p1.join()
p2.join()
if __name__ == '__main__':
main()
分布式进程
master端
import random
from queue import Queue
#BaseManager: 提供了不同机器之间共享数据的一种方法(ip:port)
from multiprocessing.managers import BaseManager
#1. 创建存储任务需要的队列
task_queue = Queue()
#2. 存储任务执行结果的队列
result_queue = Queue()
#3. 将队列注册到网上(使得其他主机也可以访问)
BaseManager.register('get_task_queue', callable=lambda : task_queue)
BaseManager.register('get_result_queue', callable=lambda : result_queue)
#绑定ip和端口, 并且来个暗号;
manager = BaseManager(address=('172.25.254.250', 4000), authkey=b'westos')
#4. 启动manager对象, 开始共享队列
manager.start()
#5. 通过网络访问共享的Queue对象;
#BaseManager.register会注册一个方法, 当调用方法时, 执行函数lambda : task_queue;
task = manager.get_task_queue()
result = manager.get_result_queue()
#6. 往队列里面放执行任务需要的数据;
for i in range(1000):
# 模拟有1000个数字;
n = random.randint(1, 100)
task.put(n)
print("任务列表中加入任务: %d" %(n))
#7. 从result队列中读取各个机器中任务执行的结果;
for i in range(1000):
res = result.get()
print("队列任务执行的result: %s" %(res))
#8. 关闭manager对象, 取消共享的队列
manager.shutdown()
slave端
import time
from multiprocessing.managers import BaseManager
#1. 连接Master端, 获取共享的队列;ip是master端的ip, port'也是master端manager进程绑定的端口;
slave = BaseManager(address=('172.25.254.250', 4000), authkey=b'westos')
#2. 注册队列, 获取共享的队列内容;
BaseManager.register('get_task_queue')
BaseManager.register('get_result_queue')
#3. 连接master端;
slave.connect()
#4. 通过网络访问共享的队列;
task = slave.get_task_queue()
result = slave.get_result_queue()
#5. 读取管理端共享的任务, 并依次执行;
for i in range(500):
n = task.get()
print("slave1 运行任务 %d ** 2: " % (n))
res = "slave1: %d ** 2 = %d" % (n, n ** 2)
time.sleep(1)
# 将任务的运行结果放入队列中;
result.put(res)
print("执行结束........")