写在前面:
前面已经有Scala实现多线程了,那么用Python实现多线程又有什么区别和好处?
Scala里实现多线程,启的是同一个Application,计算多线程任务的task都是同时返回到driver中进行管理。而这些task的完成情况和状态管理会一直保存在driver中,直到Application结束。如果模型迭代次数很多,或者需要写循环来做多次计算时,这很容易导致driver的内存溢出。
所以,为了同时实现多线程,而又不至于让Application任务太大、task太多、driver内存溢出,使用python来控制多线程,每个线程提交一个spark任务来实现一个训练任务。
当然这适用于好拆分成多个独立任务的场景。大家自己根据实际业务场景来选择。
Scala多线程之任务的异同、同步执行实践_Toby的博客-CSDN博客
python笔记9-多线程Threading之阻塞(join)和守护线程(setDaemon) - 隔壁的王先森 - 博客园
1、python实现多线程控制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time: 2021/9/26 12:02
# @Author: TobyGao
import json
import urllib2
import os, sys, datetime, time
import threading
from sys import argv
# 模型训练
def train_sh(cal_date, train_codes):
train_shell = '''sh -x run_model.sh {cal_date} {train_codes}'''.format(
cal_date=cal_date, train_codes=train_codes)
sys.stderr.write(train_shell)
state = os.system(train_shell)
if state != 0:
sys.stderr.write("model fit failed")
else:
sys.stderr.write("model fit finished")
class trainThread(threading.Thread):
def __init__(self, name, cal_date, train_codes):
threading.Thread.__init__(self)
self.threadName = name
self.cal_date = cal_date
self.train_codes = train_codes
def run(self):
sys.stderr.write("开始训练: " + self.threadName)
train_sh(self.cal_date, self.train_codes)
sys.stderr.write("结束训练: " + self.threadName)
if __name__ == '__main__':
script, calcDate = argv
# 1、特征
feature_sh = '''sh -x run_train_data.sh {calcDate}'''.format(calcDate=calcDate)
sys.stderr.write(feature_sh)
t = os.system(feature_sh)
if t != 0:
sys.stderr.write("train data failed")
else:
sys.stderr.write("train data finished")
#2、训练
cateNum = 200
batchNum = 15
threadNum = 5
i = 0
while i < cateNum:
epoch_list = []
thread_list = []
for(k in range(threadNum)):
epoch_list[k] = ""
for j in range(i, i + batchNum):
if j < cateNum:
k = j%threadNum
if epoch_list[k] == "":
epoch_list[k] = trainCodes[j]
else:
epoch_list[k] = epoch_list[k] +"," + trainCodes[j]
for(k in range(threadNum)):
thread_list[k] = trainThread("epoch %d"%k,calcDate,epoch_list[k])
thread_list[k].start()
thread_list[k].join()
i = i + batchNum
sys.stderr.write("all model fit finished")
2、sh提交spark任务代码
#!/bin/sh
export SPARK_HOME=/software/XXX/spark_3.0
if [ $# -lt 2 ]; then
echo "请添加参数 calcDate;trainCodes"
exit 1
fi
basePath=$(cd `dirname $0`; pwd)
calcDate=`date -d "$1" +%Y-%m-%d`
trainCodes=$2
echo ${calcDate}
echo ${trainCodes}
spark-submit --class com.XX.model.runModel \
--master yarn \
--deploy-mode cluster \
--driver-memory 16G \
--conf spark.driver.maxResultSize=16G \
--driver-cores 8 \
--num-executors 100 \
--executor-cores 8 \
--executor-memory 16G \
--conf spark.dynamicAllocation.enabled=true \
--conf spark.shuffle.service.enabled=true \
--conf spark.sql.shuffle.partitions=3200 \
--conf spark.default.parallelism=3200 \
--conf spark.storage.memoryfraction=0.4 \
--conf spark.shuffle.memoryFraction=0.4 \
--conf spark.sql.hive.mergeFiles=true \
--conf spark.blacklist.enabled=true \
--conf spark.speculation=true \
--conf spark.hadoop.hive.exec.orc.split.strategy=ETL \
--name run-model \
algorithms-model.jar ${calcDate} ${trainCodes}

本文对比了Python与Scala实现多线程的区别,指出在Python中使用多线程控制Spark任务可以防止driver内存溢出。通过Python的Threading库,每个线程提交一个Spark作业,适合于可拆分为独立任务的场景。文中提供了Python多线程及Spark任务提交的代码示例。

1728

被折叠的 条评论
为什么被折叠?



