1、注意:pool必须在 if name == 'main'下面运行,不然会报错
2、多进程内出现错误会直接跳过该进程,并且默认不会打印错误信息
3、if__name__下面的数据需要通过参数传入主函数里面,不然主函数获取不到该数据值而报错。
4、若不通过传参形式传入数据,可以定义全局变量。但是全局变量的值不能在多进程里面进行修改。

import threading
import multiprocessing
import os  # 用于获取当前执行的文件名
import time, random
import traceback  # 用于捕获异常
import sys  # 用于捕获异常


def main_func(i):  # 多进程运行的函数
    try:  # 多进程中发生异常是不会打印错误信息,并且当前进程会直接跳过,所以异常需要自行捕获
        rand_time = random.randint(1, 3)  # 随机产生1~3的整数
        time.sleep(rand_time)
        print(i)
    except:
        current_filename = str(os.path.basename(sys.argv[0]))[:-3]  # 获取当前文件名称
        cur_err_filname = current_filename + '_error.txt'
        error_info = sys.exc_info()  # 打印异常
        with open(f'{cur_err_filname}', 'a') as f:
            error_str = f'{i}:ERROR OCCURRED,{time.strftime("%Y-%m-%d %H:%M:%S")}:\n {error_info[0]}: {error_info[1]}'  # 记录当前进程特征值,错误发生时间 ,错误类型,错误概述
            print(error_str, file=f)  # 通过打印方式写入文件
            traceback.print_tb(error_info[2], file=f)  # 错误细节描述(包括bug的代码位置)
            f.write(f"{'=' * 50}\n")  # 分行


if __name__ == '__main__':  # 必须在此语句下面运行,不然会报错
    ## 普通进程
    # 三种模式:fork、spawn, forkserver
    """
    spawn:父进程启动一个新的Python解释器, 子进程将只继承运行run()方法所需的资源。
          不继承父进程不必要的文件描述符和句柄(一种特殊的只能指针)。
          与使用fork或forkserver相比,使用此方法启动进程相当慢。
          在Unix和Windows上可用,Windows上为默认
          不会拷贝主进程内的数据,如果需要使用主进程的值,需要通过传参的方式将数据传入子进程。
    fork:父进程使用os.fork()来分叉Python解释器,
          子进程开始时,与父进程实际上是相同的。
          父进程所有资源都由子进程继承。这不能保证多线程的安全问题。
          会拷贝主进程上的数据到子进程中,并且不共享内存。
    forkserver:当程序启动并选择forkserver start方法时,将启动服务器进程。
                从那时起,每当需要新进程时,父进程连接到服务器,并请求它分配一个新进程。
                叉服务器进程是单线程的,因此它可以安全使用os.fork()。
                没有不必要的资源被继承。和spawn类似,一些数据需要通过参数传递。
    """
    multiprocessing.set_start_method("fork")
    p1 = multiprocessing.Process(target=main_func, args=(2,))  # 定义进程
    p1.start()  # 启动进程
    
    # 查看进程id
    os.getpid()  # 当前进程id
    os.getppid()  # 父进程id
    
    # 查看当前进程中的线程数量
    len(threading.enumerate())
    
    ## 多进程
    process_list = []
    for i in range(10):
        p = multiprocessing.Process(target=main_func, args=(2,))
        p.start()
        process_list.append(p)
    # spawn模式需要加上以下代码:等所有进程循环以后,再join,不然就不是多进程了
    # 注:fork模式不用加以下代码
    # for item in process_list:  
    #     item.join()
    
    
    ## 进程池使用
    """
    一般来说,cpu有多少个,就可以创建多少个进程
    进程池比上面的创建多个进程有优势,因为上面创建了多个进程,但实际使用的只有cpu的个数
    而进程池是,给定进程数量,就创建那么多进程,等老的进程执行完了以后,再创建新的进程
    进程池方法一:
    """
    # 查看cpu个数,计算机管理 --> 处理器,有多少行,就有多少个
    # 也可以通过以下命令查看
    from multiprocessing import Pool  # 进程池,用于多进程
    multiprocessing.cpu_count()
    pool = Pool(5)  # 定义进程数量 
    for i in range(20):
        pool.apply_async(main_func, (i,))  # 调用函数执行多进程
    pool.close()  # 关闭进程池
    pool.join()  # 阻塞进程,此两部不能少,保证多进程正常运行 
    
    """
    进程池方法二:
    注:python3有线程池,使用方法和进程池类似
    """
    from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
    def done(res):
        data = res.result()  # 进程返回的值在此函数中处理
        print(data)
    pool = ProcessPoolExecutor(4)
    for i in range(10):
        fur = pool.submit(main_func, (i, ))
        fur.add_done_callback(done)  # done 的调用由主进程处理(与线程池不同,线程池是由子线程完成这个回调任务,可以通过打印进程号来查看)
    pool.shutdown(True)  # 让进程卡在此处,等所有进程执行完毕以后,再执行后面的
    
    
    
    ## 进程之间数据共享问题
    """
    1、使用 multiprocessing.Values/Array 的方法来定义和赋值数据,底层基于c语言的,用着不太习惯,了解即可
    2、使用列表、字典,将外面的字典传入进程,在进程里面对列表数据进行添加修改等操作,数据是共享的
    3、使用 queue = multiprocessing.Queue()队列,在主进程中放入数据,将数据传入子进程,在子进程中是可以获取到父进程的数据的
        子进程中 queue.put()放入数据,父进程中queue.get()就可以取到值
        反过来,主进程中put,子进程中get也可以
    4、基于 parents_data, child_data = multiprocessing.Pipe()。
        通过将child_data传入子进程,进程内child_data.recv()就可以获取父进程发送的数据
        通过child_data.send(data),就可以将数据发送给父进程,然后parents_data.recv()就可以收到子进程发送的数据
    5、基于文件、数据库进行数据共享
    """
    
    ## 进程锁:用于解决进程数据共享问题,与多线程类似
    # 使用方法与多线程一样,lock通过参数传递进子进程
    # lock = multiprocessing.RLock()
    # lock.acquire()
    # lock.release()
    
    # 进程池的进程锁,需要基于Manager中的Lock和RLock
    # 普通进程直接使用上面的Lock即可
    # manager = multiprocessing.Manager()
    # lock = manager.RLock()

posted on 2020-03-28 10:59  jaysonteng  阅读(899)  评论(0编辑  收藏  举报