使用 Python 做任务并行~灾过的坑

起初的串行调度

串行调度,主要是执行效率低、没有充分利用资源。

串行调度的目的,主要是为了发现每一步操作的耗时问题。

Python 的多线程

当任务串行成功时,考虑使用并行方式,提高操作的效率。

使用背景:

  • 同时可以运行 3 个任务,分别称为 convertA, convertB, convertC
    • 每个任务内有系列的计算,需要串行

使用 Threading 效果远低于串行调度:

  • 使用 threading 模块 threading.Thread() 创建线程,其中 target 参数值为需要调用的方法,同样将其他多个线程放在一个列表中,遍历这个列表就能同时执行里面的函数了
start = time.time()

threads = [threading.Thread(target=convertA, args=(sql_jar_path, odpscmd, replace_data, )),
           threading.Thread(target=convertB, args=(sql_jar_path, odpscmd, replace_data, )),
           threading.Thread(target=convertC, args=(sql_jar_path, odpscmd, replace_data, ))]

for t in threads:
    # 启动线程
    t.start()

end = time.time()
print(str(round("total time:", end-start,3))+'s')
  • 使用串行方式和上述 threading 方式对比,串行时间用时 1736 s、threading 用时 2790 s

    • 多线程比单线程慢的原因是,Python 的全局锁 GIL 导致多线程没有利用多核 CPU 的优势,且涉及到线程切换

全局锁 GIL

  • 解释器的 C 语言实现内容,并行执行时线程不安全
    • 因此通过 GIL 保护解释器,确保一个进程内只有一个线程执行
    • GIL 的最大问题就是 Python 的多线程程序不能利用多核 CPU 的优势
      • 使用 Python 多线程的程序只会在一个 CPU 核上运行
    • GIL 只影响严重依赖 CPU 的程序
      • 若程序大部分只涉及到 I/O,使用多线程很合适,因为线程大部分时间在等待
      • 对于依赖 CPU 的程序,需要弄清楚执行的计算特点
  • 注意:线程不是专门用来优化性能的
    • 一个 CPU 依赖程序可能会使用线程来管理图形用户界面、网络连接或其他服务
      • 此时若一个线程长期拥有 GIL,会导致其他非 CPU 型线程一直等待
  • 解决 GIL 的缺点,如下提供两种策略

Python 的多进程 multiprocessing.Pool

  • 任务并行操作
print('Parent process %s.' % os.getpid())
p = Pool(4)
p.apply_async(convertA, args=(sql_jar_path, ods, replace_data, ))
p.apply_async(convertB, args=(sql_jar_path, ods, replace_data, ))
p.apply_async(convertC, args=(sql_jar_path, ods, replace_data, ))

p.close()
p.join()
print('All subprocesses done.')
  • subprocess 的使用

    • subprocess 模块是 Python 2.4 版本开始引入的模块,主要用来取代 一些旧的模块方法,如 os.systemos.spawnos.popencommands.*等.

    • 通过子进程来执行外部命令,并通过 input/output/error 管道,获取子进程的执行返回信息

# 方式 1:命令行拼接
subprocess.run([cmd_name, running_args, new_osql_path])

# 方式 2:直接使用命令行的脚本,需要 subprocess 去解析命令
cmdstr = spark_path + " --files " + upload_files + " " \
         + " --class " + class_name + " " + jar_path + " " + parameters
print(cmdstr)
subprocess.run(cmdstr, shell=True)

C 扩展编程

使用 C 计算密集型任务,跟 Python 独立,在工作的时候在 C 代码中释放 GIL

#include "Python.h"
...

PyObject *pyfunc(PyObject *self, PyObject *args) {
   ...
   Py_BEGIN_ALLOW_THREADS
   // Threaded C code
   ...
   Py_END_ALLOW_THREADS
   ...
}

参考文献

1、multiprocessing 并行

2、Python的全局锁问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值