一、多进程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 Thread 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 Thread 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 Thread 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比较来决定是否处理