程序:例如xxx.py这是程序,是一个静态的
进程:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。
不仅可以通过线程完成多任务,进程也可以。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。
def sing():
print(f'开始唱歌。。。{ctime()}')
sleep(3)
print(f'结束唱歌。。。{ctime()}')
def dance():
print(f'开始跳舞。。。{ctime()}')
sleep(3)
print(f'结束跳舞。。。{ctime()}')
if __name__ == '__main__':
print(f'程序开始--------------{ctime()},pid={os.getpgid()},ppid={os.getppid()}')
p1 = multiprocessing.Process(target=sing)
p2 = multiprocessing.Process(target=dance)
p1.start()
p2.start()
p1.join()
print(f'程序结束-------------{ctime()},pid={os.getpgid()},ppid={os.getppid()}')
Process([group [, target [, name [, args [, kwargs]]]]])
Process创建的实例对象的常用方法:
Process创建的实例对象的常用属性:
def func(name,age,**kwargs):
for i in range(10):
print(f'子进程运行。。name={name},age={age},pid={os.getpid()}')
print(kwargs)
i += 1
print('次数', i)
time.sleep(0.2)
if __name__ == '__main__':
p = multiprocessing.Process(target=func,args=('小埋',18),kwargs={
'home':'广州'})
p.start()
time.sleep(2)
p.terminate()# 立即结束此进程
g_lis = [11,22,33]
def work1():
g_lis.append(44)
print(f'--------work1中,g_lis={g_lis}')
def work2():
print(f'--------work2中,g_lis={g_lis}')
if __name__ == '__main__':
print(f'子进程创建之前,g_lis:{g_lis}')
p1 = multiprocessing.Process(target=work1)
p1.start()
p1.join()
p2 = multiprocessing.Process(target=work2)
p2.start()
q = multiprocessing.Queue(3) # 初始化一个Queue对象,最多可以接收3条消息
q.put('消息1')
q.put('消息2')
print(q.full())
q.put('消息3')
print(q.full())
# try:
# q.put('消息',True,2)
# except Exception as e:
# print(e)
if not q.empty(): # 也可以用while循环
for i in range(q.qsize()):
str1 = q.get()
print(str1)
如果设置超时时间,在放入信息时,会等待相应的时间,
如结束时还没有空间,则抛出异常。
如果第二个参数(block)为False,队列没有空间,则立即抛出异常。
如果第二个参数(block)为默认值(True)且没有设置超时时间,消息会阻塞,
直到有空间。
def consumer(q,name):
while True:
food = q.get()
if not food:
print('已经吃完了。。。')
break
print(f'{name}消费了{food}')
time.sleep(random.randint(0,5))
q.task_done() # 向生产者发送信号,消费了Q队列里的一个信息
def producer(name,q,food):
for i in range(10):
f = f'{name}生产了{food}{i}'
time.sleep(random.randint(0, 5))
print(f)
q.put(food)
q.join() # 当生产者生产完毕之后,会阻塞,等消费者消费
if __name__ == '__main__':
q = multiprocessing.JoinableQueue()
p1 = multiprocessing.Process(target=producer,args=('娃哈哈',q,'包子'))
p2 = multiprocessing.Process(target=producer,args=('旺仔',q,'馒头'))
p1.start()
p2.start()
c1 = multiprocessing.Process(target=consumer,args=(q,'小埋'))
c2 = multiprocessing.Process(target=consumer,args=(q,'巫马鹿'))
c1.daemon = True
c2.daemon = True
c1.start()
c2.start()
# 主程序等待生产者全部生产完毕
p1.join()
p2.join()
# 朝队列中放入两个指示性空值
# q.put(None)
# q.put(None)
print('主程序结束')
如果某个进程的daemon属性为False,主进程结束时,会检测该进程,是否结束。如果该进程还在运行,主进程会等待它完成之后再弹出
如果某个进程的daemon属性为True,主进程结束时,不会检测该进程是否结束,而直接结束,主进程会等待它完成之后再弹出,并且所有daemon属性为True的进程会直接关闭,不论是否运行完毕。
除了使用Process创建进程外,还可以使用进程池Pool创建,前者适用于处理少量进程,后者适用于处理大量进程,免去编写过多进程的苦恼。
顺带提一下Pipe()管道。
def read(p1):
print(f'read启动pid={os.getpid()}')
print(f'read读取到{p1.recv()}') # recv从管道中读取数据
def write(p2,i):
print(f'write启动pid={os.getpid()}')
p2.send(f'{i}python') # 向管道中发送数据
print(f'放入消息{i}python')
if __name__ == '__main__':
po = Pool()
p1,p2 = Pipe() # 生产一个管道,一段可以放入数据,一段可以读取数据
for i in range(3):
po.apply_async(read,(p1,))
for i in range(3):
po.apply_async(write,(p2,i))
po.close()
po.join()
def copy_file(p1,file_name,source_f,dest_f):
with open(source_f+'/' + file_name,'rb') as fr,open(dest_f + '/' + file_name,'wb') as fw:
while True:
content = fr.read(1024) # 读取原文件
fw.write(content) # 写入新文件
if len(content) < 1024:
break
p1.send(file_name) # 将已备份好的文件名放入管道
def main():
source_f = input('请输入要备份的目录名')
# 生成新的目录名
dest_f = source_f+'[备份]'
if not os.path.exists(dest_f):
# 创建目录
os.mkdir(dest_f)
file_name_list = os.listdir(source_f) # 获取原目录中所有文件名
p1,p2 = multiprocessing.Pipe()# 创建管道
pool = multiprocessing.Pool(3) # 创建进程池
for file_name in file_name_list:
pool.apply_async(copy_file,(p1,file_name,source_f,dest_f))
pool.close() # 不再添加任务
all_num = len(file_name_list)
while 1:
# 获取已经完成的文件
file_name = p2.recv()
if file_name in file_name_list:
file_name_list.remove(file_name)
# 进度
copy_rate = (all_num - len(file_name_list)) / all_num
print(f'复制进度{file_name}{copy_rate:.0%}')
# 如果进度达到百分百,则跳出
if copy_rate >= 1:
break
if __name__ == '__main__':
main()