互联网上介绍多进程的文章很多,比如Python多进程 - 实现多进程的几种方式、python多进程并发、多进程。为什么自己记录呢,是因为在做多进程的时候总会遇到这样那样的问题,故只好亲自实践一下。
1 最简单的多进程
使用multiprocessing.Process定义进程,target
参数传递的是进程执行的方法,args
则是传递给执行方法的参数,这个参数不要太复杂,复杂类型就可能会报错。
import multiprocessing
if __name__ == '__main__':
sub_list = get_sub_list()
p0 = multiprocessing.Process(target=to_total, args=(sub_list[0],))
p1 = multiprocessing.Process(target=to_total, args=(sub_list[1],))
# 开启进程
p0.start()
p1.start()
# 阻塞进程
p0.join()
p1.join()
执行中,可以在windows任务管理器中看到两个python进程在运行了。从监控中,也可以看到问题。我们知道进程是资源分配的最小单位,而python启动两个进程后,CPU、内存基本没什么消耗,而我是处理几万的数据,那么怎样让CPU忙碌起来呢?及时启动再多进程也没有办法,因为我的业务逻辑是读取两个es索引的数据,进行处理,我还不能防止重复。
2 多进程(生产者-消费者模式)
难道真的没有办法吗?“线程是CPU调用的最小单位”,是否可以利用线程提供资源利用率呢?多线程还是多进程的选择及区别
python并发编程之多进程、多线程、异步和协程,从这些文章可以看出来,如果我想采用多进程、多线程的编程模式,我的代码逻辑必须调整。
原先简单的多进程逻辑to_total
的代码逻辑是
# 从es中分片读取数据
# 将读取后的数据进行业务处理并存入到数据库中
但看了为什么在Python里推荐使用多进程而不是多线程?和Python中单线程、多线程和多进程的效率对比实验,在python中使用多进程比多线程要好。那么我是不是可以利用生产者-消费者模式,做多进程呢?
代码并不难,仿照python并发编程:多进程-生产者消费者模型,写了一个:
'''
多进程(生产者-消费者)
'''
import time
import random
import pymongo
from elasticsearch import Elasticsearch
from elasticsearch import helpers
from redis.sentinel import Sentinel
from multiprocessing import Process, JoinableQueue
def consumer(q,i):
'''
消费者
:return:
'''
print('消费进程%d启动' % i)
es = get_es()
mongo = get_mongo()
esService = getEsService(es,mongo)
while True:
item = q.get()
print('消费进程%d数据' % i)
time.sleep(random.randint(1, 3))
esService.handler(item)
q.task_done()
def producer(q,sub_index):
'''
生产者
:return:
'''
#
print('生产进程%s启动' % sub_index)
es = get_es()
#
body = {
"query": {
"match_all": {
}
}}
results = helpers.scan(
client=es,
query=body,
scroll="5m",
index=sub_index,
doc_type='test_type',
timeout="10m"
)
for result in results:
name = result.get('_source').get('name')
date = result.get('_source').get('date')
source = result.get('_source').get('source')
item = {'name': name,
'date': date, 'source': source}
if 'status' in result.get('_source'):
item['status'] = result.get('_source').get('status')
q.put(item)
q.join()
def get_es():
if ES_USER:
es = Elasticsearch([ES_IP], http_auth=(ES_USER, ES_PASSWORD),
port=9200, timeout=50000,
sniff_on_start=True, sniff_on_connection_fail=True, max_retries=3, retry_on_timeout=True)
else:
es = Elasticsearch([ES_IP], sniff_on_start=True, sniff_on_connection_fail=True,
max_retries=3,
retry_on_timeout=True)
return es
def get_mongo():
mongo_url = 'mongodb://10.101.3.190:27017'
mongo = pymongo.MongoClient(mongo_url)
return mongo
def getEsService(es, mongo):
# reids
params = {'db': 1}
sentinel = Sentinel(REDIS_SENTINELS, **params)
server = sentinel.master_for(REDIS_PARAMS['service_name'], **params)
bot_name = 'en_total'
# es
return EsService(es=es,mongo=mongo['en_invalid'],redis=server)
def get_sub_list():
sub_list = []
sub_list.append('test01')
sub_list.append('test02')
sub_list.append('test03')
sub_list.append('test04')
sub_list.append('test05')
sub_list.append('test06')
return sub_list
if __name__ == '__main__':
q = JoinableQueue()
sub_list = get_sub_list()
ps = []
for sub in sub_list:
p = Process(target=producer,args=(q,sub))
ps.append(p)
# 生产者启动
for p in ps:
p.start()
# 消费者启动
cs = []
for i in range(20):
c = Process(target=consumer, args=(q,i ))
c.daemon = True
cs.append(c)
for c in cs:
c.start()
# 生产者阻塞
for p in ps:
p.join()
运行成功
但是在资源管理器中查看,为什么进程消耗的CPU为0呢?
3 进程池
参考python3.x pool.map方法的实质,关于性能的描述可以参考
python 性能提升之 并行map
import multiprocessing
pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
pool.map(cal_each, [row for ix, row in df.iterrows()])
pool.close()
pool.join()
def cal_each(row):
pass
4 多线程
Process Explorer v16.43
安装过程参考windows查看线程工具
Python 异步 IO(asyncio)、多进程(multiprocessing)、多线程(multithreading)性能对比
from life_example.service.example_service import ExampleService
import asyncio
import multiprocessing
async def parse_bazi(bazi):
print(bazi)
gzShishen = parse(bazi)
exampleService.save_gz_shishen(gzShishen)
def parse_with_process(day):
bazis = exampleService.get_bazi_by_day(day)
loop = asyncio.get_event_loop()
tasks = []
for bazi in bazis:
tasks.append(parse_bazi(bazi))
loop.run_until_complete(asyncio.wait(tasks))
def run_by_process():
day_list = ['甲子','乙丑','丙寅','丁卯','戊辰','己巳','庚午','辛未','壬申','癸酉']
pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
result = pool.map(parse_with_process,day_list)
print(result)