首页 > 技术文章 > python的多进程、logging模

jiangzuofenghua 2019-09-03 10:01 原文

一、多进程multiprocessing

1、Process类

  • Process类遵循了Thread类的API,减少了学习的难度
举例 :单线程、多线程和多进程的比较

    import multiprocessing
    import datetime

    #计算
    def calc(i):
        sum = 0
        for _ in range(1000000):
            sum += 1

        print(i, sum)

    if __name__ == "__main__":
        start = datetime.datetime.now()
        ps = []
        for i in range(5):
            #多进程
            p = multiprocessing.Process(target=calc, args=(i,), name="cal-{}".format(i))
            ps.append(p)
            p.start()
        for p in ps:
            p.join()

        delta = (datetime.datetime.now() - start).total_seconds()
        print(delta)
        print('===end===')



    单线程,多线程都跑了4分钟,而多进程用了1分钟,这是真并行
    

2、进程间同步

  • 进程间同步提供了和线程同步一样的类,使用的方法一样,使用的效果也类似,不过,进程间代价要高于线程,而且底层实现是不同的,只不过python屏蔽了这些,让用户简单使用
  • multiprocessing还提供共享内存,服务器进程来共享数据,还提供了Queue队列,Pipe管道用于进程间通信

3、通信方式不同

  • 多进程就是启动多个解释器进程,进程间通信必须序列化,反序列化
  • 数据的线程安全性问题

4、进程池的举例

进程池的举例
    
import multiprocessing
import datetime

#计算
def calc(i):
    sum = 0
    for _ in range(1000000):
        sum += 1
    print(i, sum)

if __name__ == "__main__":
    start = datetime.datetime.now()
    p = multiprocessing.Pool(5)
    for i in range(5):
        p.apply_async(calc, args=(i,))
    p.close()
    p.join()
    delta = (datetime.datetime.now() - start).total_seconds()
    print(delta)
    print('end============')

5、多进程,多线程的选择

  • CPU密集型:CPython中使用到了GIL,多线程的时候锁互相竞争,且多核优势不能发货,Python多进行效率更高
  • IO密集型:合适是用多线程,减少IO序列化开销,且在IO等待的时候,切换到其他线程继续执行,效率不错

 

二、logging模块

  •  日志级别 数值
  •  CRITICAL  50
  •  ERROR     40
  •  WARNING   30,默认级别
  •  INFO      20
  •  DEBUG     10
  •  NOTSET    0
  • 日志级别指的是生产日志的事件的严重程度,设置一个级别后,严重程度低于设置值的日志消息将被忽略

1、格式字符串

  • 1、日志消息内容 %(message)s :当调用Formatter.format()时设置
  • 2、asctime      %(asctime)s : 创建LogRecord时的可读时间,默认情况下,它的格式为时间
  • 3、函数名       %(funcName)s :日志调用所在的函数名
  • 4、日志级别名称 %(levelname)s : 消息的级别名称'DEBUG','INFO','WARNING','ERROR','CRITICAL'
  • 5、模块         %(module)s    : 模块名
  • 6、进程ID       %(process)d   :  进程ID
  • 7、进程名称     %(processName)s : 进程名
  • 8、线程ID       %(thread)d    : 线程ID
  • 9、线程名称     %(threadName)s : 线程名字

2、默认级别 举例

    import logging

    FORMAT  = '%(asctime)-15s\tThread  info: %(thread)d %(threadName)s %(message)s'
    logging.basicConfig(format=FORMAT)

    logging.info('I am {}'.format(20))   #info不显示
    logging.warning('I am {}'.format(20))   #warning默认级别

3、构建消息

import logging

    FORMAT  = '%(asctime)-15s\tThread  info: %(thread)d %(threadName)s %(message)s'
    logging.basicConfig(format=FORMAT,level=logging.INFO)


    logging.info('I am {}'.format(20))   #单一字符串
    logging.warning('I am %d %s', 20, 'years old')   #c风格

    上例是基本的使用方法,大多数时候,使用的是info,正常运行信息的输出

4、日志级别和格式字符串扩展的例子

import logging

    FORMAT  = '%(asctime)-15s\tThread info: %(thread)d %(threadName)s %(message)s %(school)s'
    logging.basicConfig(format=FORMAT,level=logging.WARNING)

    d = {'school': 'magedu.com'}

    logging.info('I am %s %s', 20, 'years old', extra=d)
    logging.warning('I am %s %s', 20, 'years old', extra=d)   #c风格

5、输出到文件

    import logging
    logging.basicConfig(format='%(asctime)s %(message)s', filename='c:/test')
    for _ in range(5):
        logging.warning('this event was logged')

6、Logger类

  •  使用工厂方法返回一个Logger类:logging.getLogger([name=None])
  •  指定name,返回一个名称为name的Logger实例,如果再次使用相同的名字,是实例化一个对象
  •  未指定name,返回Logger实例,名称是root,即根Logger
  •  Logger是层次结构的,使用.点号分割,如'a','a.b'或’a,b,c,d',a是a.b的父parent

7、总结

  • 每一个Logger实例的level如同入口,让水流进来,如果这个门槛太高,信息就进不来;例如log3.warning('log3),如果log3定义的级别高,就不会有信息通过log3
  • 如果level没有设置,就用父logger,如果父logger的level没有设置,继续找父的父的,最终可以找到root上,如果root设置了就他的,如果没有设置,root的默认值是WARNINF
  • 消息传递流程:
  •   在某个logger上产生某种级别的消息,首先和logger的level检查,如果消息level低于logger
  •   的EffectiveLevel有效级别,消息丢弃,如果通过检查后,消息交给交给logger所有的handler处理,
  •   每一个handler需要和自己的level比较来决定是否处理

 

推荐阅读