高级编程技巧——协程

9 协程

9.1 多线程共享全局变量

import threading
import time
import multiprocessing

num=100
def demo1():
    global num
    num+=1
    print(f'{num}')

def demo2():
    print(f'{num}')

def main():
    # 线程能共享局部变量
    # t1=threading.Thread(target=demo1)
    # t2=threading.Thread(target=demo2)
    #
    # t1.start()
    # t2.start()

    # 进程不能共享全局变量
    p1=multiprocessing.Process(target=demo1)
    p2=multiprocessing.Process(target=demo2)

    p1.start()
    p2.start()

if __name__=='__main__':
    main()

9.2 进程间的通信

在这里插入图片描述
在这里插入图片描述

  • 这个时候就可以使用到队列进行通信

  • 需求;
    - 实现在函数download中,将list函数保存在队列当中。
    - 实现函数 manage_data 中,将list元素从队列中取出,并且添加到新的列表中。

import multiprocessing
'''
使用进程的队列实现进程间的通信
'''
def download(q):
    lis=[1,2,3]
    for i in lis:
        q.put(i)
    print('添加完毕')
def manage_data(q):
    # 将队列里面的元素提取出来,存到新的列表中
    man_data=list()
    while True:
        data=q.get()
        man_data.append(data)

        # 如果队列为空 则退出循环
        if q.empty():
            break
    print(man_data)

def main():
    q=multiprocessing.Queue()
    p1=multiprocessing.Process(target=download,args=(q,))
    p2 = multiprocessing.Process(target=manage_data, args=(q,))

    p1.start()
    p2.start()

if __name__=='__main__':
    main()

import multiprocessing
import queue
def download(q):
    lis=[1,2,3]
    for i in lis:
        q.put(i)
    print('添加完毕')
def manage_data(q):

    man_data=list()
    while True:
        data=q.get()
        man_data.append(data)

        if q.empty():
            break
    print(man_data)

def main():
    # q=multiprocessing.Queue()

    # 1.使用普通队列
    q=queue.Queue()
    p1=multiprocessing.Process(target=download,args=(q,))
    p2 = multiprocessing.Process(target=manage_data, args=(q,))

    # 使用普通队列的时候 不再使用start() 启动进程 而是使用run() 方法
    # p1.start()
    # p2.start()

    p1.run()
    p2.run()
if __name__=='__main__':
    main()
  • 注意:如果使用普通队列,不是使用start()方法,而是使用run()方法。

startt() 与 run() 区别

  • start() 方法来启动进程,真正实现了**多进程运行,**这是无需等待run() 方法体代码执行完毕而直接继续执行下面的代码:调用Process 类的start () 方法来启动一个进程,这时此进程处于就绪(可运行)状态,并没有运行,一旦得到 cpu 时间片,就开始执行 run() 方法,这里方法 run () 称为进程体,当进程结束后,不可以重新启动。
  • run() 方法只是类的一个普通方法,如果直接调用run () 方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待 run 方法体执行完毕后才可以继续执行下面的代码,这样就没有达到写线程的目的。

进程池
当要创建的子进程数量不多时,可以直接利用 multiprocessing 中的 Process 动态生成多个进程,但是如果是上百甚至上千个目标,手动的去创建的进程的工作量巨大,此时就可以用到 multiprocessing 模块提供的 Pool方法,也就是进程池。

在这里插入图片描述

  • 初始化 Pool 时,可以指定一个最大进程数,当有新的请求提交到Pool 中时,如果池还没有满,那么就会创建一个新的进程池用来执行该请求,但是如果进程池中的进程已经达到指定的最大值,那么该请求 就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。

如下代码,使用进程池体现。

import os,time
from multiprocessing import Pool

def worker(msg):
    # 记录开始时间
    t_start=time.time()

    # 打印输出 任务对应的进程ID
    print(f'{msg}开始执行,进程号为{os.getpid()}')

    time.sleep(2)
    # 记录开始时间
    t_stop = time.time()

    # 输出任务时
    print(msg,f'执行完成,耗时{t_stop-t_start}')

if __name__=='__main__':
    # 1.创建进程池
    po = Pool(3)

    for i in range(10):
        po.apply_async(worker,(i,))

    # 此时运行,只执行了主进程
    print('start')
    po.close()

    # 关闭进程池 在添加 进程任务 运行报错
    # po.apply_async(worker)  # ValueError: Pool not running
    po.join()

进程池间通信

  • 使用进程池通信时,要使用进程池的队列。
  • multiprocessing.Manager().Queue()
import multiprocessing

def demo1(q):
    try:
        # 注意:进程池里面的进程,如果出现异常,并不会报出现异常 而是阻塞
        1/0
        q.put('a')
        print('--1--')

    except Exception as e:
        print(e)

def demo2(q):
    print(q.get())
    print('--2--')

if __name__=='__main__':
    # 1.进程池队列通信 进程池 使用进程池队列 没有调用的 demo1 与 demo2
    # q=multiprocessing.Queue()

    # 1.进程池创建队列通信
    q=multiprocessing.Manager().Queue()

    # 2.进程池
    po = multiprocessing.Pool()

    # 3.添加任务
    po.apply_async(demo1,args=(q,))
    po.apply_async(demo2,args=(q,))

    po.close()

    po.join()

9.3 多任务文件夹复制

需求 1:实现多任务文件夹复制

  • 获取用户要复制的文件夹名字
  • 创建一个新的文件夹
  • 获取文件夹所有带拷贝的文件名字
  • 创建进程池
  • 添加拷贝任务
import multiprocessing
import os
def copy_file(file_name,new_fold_name,old_fold_name):
    # 读取 file_name 文件内容
    with open(old_fold_name+'/'+file_name,'rb') as f:
        content=f.read()

    # 保存到新的文件夹中
    with open(new_fold_name + '/'+ file_name,'wb') as n_f:
        n_f.write(content)
def main():
    # 1.获取用户要复制的文件夹名称
    old_fold_name=input('请输入要复制的文件夹名字:')

    # 2.创建新的文件夹
    new_fold_name=old_fold_name+'附件'

    # 3.判断该文件路径是否存在 不存在才 创建文件夹
    if not os.path.exists(new_fold_name):
        os.mkdir(new_fold_name)

    # 4.获取老的文件夹 下 所有需要被拷贝文件的 名字
    file_names=os.listdir(old_fold_name)
    # print(file_names)

    # 同步 拷贝
    # 5.创建进程池
    po=multiprocessing.Pool()

    # 6.添加拷贝任务
    for file_name in file_names:
        po.apply_async(copy_file,args=(file_name,new_fold_name,old_fold_name))
    po.close()
    po.join()
if __name__=='__main__':
    main()

需求 2:实现进度条

import multiprocessing
import os
def copy_file(q,file_name,new_fold_name,old_fold_name):
    # 读取 file_name 文件内容
    with open(old_fold_name+'/'+file_name,'rb') as f:
        content=f.read()

    # 保存到新的文件夹中
    with open(new_fold_name + '/'+ file_name,'wb') as n_f:
        n_f.write(content)

    # 将拷贝到的文件放置队列
    q.put(file_name)
def main():
    # 1.获取用户要复制的文件夹名称
    old_fold_name=input('请输入要复制的文件夹名字:')

    # 2.创建新的文件夹
    new_fold_name=old_fold_name+'附件'

    # 3.判断该文件路径是否存在 不存在才 创建文件夹
    if not os.path.exists(new_fold_name):
        os.mkdir(new_fold_name)

    # 4.获取老的文件夹 下 所有需要被拷贝文件的 名字
    file_names=os.listdir(old_fold_name)
    # print(file_names)

    # 同步 拷贝
    # 5.创建进程池
    po=multiprocessing.Pool()

    # 创建队列
    q=multiprocessing.Manager().Queue()

    # 6.添加拷贝任务
    for file_name in file_names:
        po.apply_async(copy_file,args=(q,file_name,new_fold_name,old_fold_name))
    po.close()

    # 1.文件总数
    file_count=len(file_names)

    copy_file_num=0
    while True:
        q.get()
        copy_file_num+=1
        print('拷贝进度%2.f%%'%(copy_file_num*100/file_count))

        if copy_file_num>=file_count:
            break
    po.join()
if __name__=='__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值