|
|
51CTO旗下网站
|
|
移动端

2.4.2 多进程 VS 多线程

《量化交易之路:用Python做股票量化分析》第2章量化语言——Python,第2部分(第2~6章)主要讲解了量化交易需要的基础知识及相关工具,如Python语言、NumPy、pandas、数据可视化及量化数学等知识,适合完全没有任何编程经验的读者从头开始阅读。本节为大家介绍多进程 VS 多线程。

作者:阿布来源:机械工业出版社|2017-10-19 16:44

2.4.2  多进程 VS 多线程

2.4.1节的笛卡尔积对两个参数集合排列组合总共有154种组合方式,由于这里的简单回测并没有运行复杂的计算,也没有繁多的I/O操作,所以通过for循环串行计算每组参数的结果也没有速度上的问题。真实的回测中不但有复杂的计算,繁多的I/O操作且回测本身的复杂度也不是2.4.1节中的示例能比拟的。

针对上面的问题,一般使用多任务并行的方式来解决,有如下几种方式:

启动多个进程;

启动多个线程;

启动多个进程,每个进程启动多个线程。

使用哪种方式最好呢?

由于全局解释锁GIL,Python的线程被限制为同一时刻只允许一个线程执行,所以Python的多线程适用于处理I/O密集型任务和并发执行的阻塞操作,多进程处理并行的计算密集型任务。

Python中实现多任务的库有很多,可以通过几行代码就完成一个并行任务,由于篇幅有限,这里只示例使用concurrent.futures库。

1.使用多进程(ProcessPoolExecutor)

下面使用(ProcessPoolExecutor)来完成与2.4.1节寻找最优参数相同的任务

  1. result = []  
  2. # 回调函数,通过add_done_callback任务完成后调用  
  3. def when_done(r):  
  4.     # when_done在主进程中运行  
  5.     result.append(r.result())  
  6. """  
  7.     with class_a() as a: 上下文管理器:稍后会具体讲解  
  8. """  
  9. with ProcessPoolExecutor() as pool:  
  10.     for keep_stock_threshold, buy_change_threshold in \  
  11.             itertools.product(keep_stock_list, buy_change_list):  
  12.         """  
  13.             submit提交任务:使用calc()函数和的参数通过submit提交到独立进程  
  14.             提交的任务必须是简单函数,进程并行不支持类方法、闭包等,  
  15.             函数参数和返回值必须兼容pickle序列化,因为进程间的通信需要传递可序列化对象  
  16.         """  
  17.         future_result = pool.submit(calc, keep_stock_threshold,  
  18.                                     buy_change_threshold)  
  19.         # 当进程完成任务即calc运行结束后的回调函数  
  20.         future_result.add_done_callback(when_done) 

下面使用多进程并行方式的结果和上面for循环串行计算的结果是一致的,但是针对本例来说,运行耗时的多进程的并行操作反而会更慢,因为进程的创建、销毁,以及进程之间的通信都要有一定的开销。

  1. sorted(result)[::-1][:10] 

输出如下:

  1. [(0.5790000000000001, 28, -0.1),  
  2.  (0.519, 26, -0.1),  
  3.  (0.5019999999999999, 28, -0.05),  
  4.  (0.4770000000000001, 24, -0.1),  
  5.  (0.4660000000000001, 22, -0.1),  
  6.  (0.45100000000000007, 16, -0.09),  
  7.  (0.44999999999999996, 20, -0.06),  
  8.  (0.44800000000000006, 28, -0.07),  
  9.  (0.437, 28, -0.13),  
  10.  (0.437, 28, -0.14)] 

2.使用多线程ThreadPoolExecutor

使用多线程ThreadPoolExecutor与前面使用多进程的方法几乎一样,唯一的区别是使用ThreadPoolExecutor代替ProcessPoolExecutor。

  1. from concurrent.futures import ThreadPoolExecutor  
  2. result = []  
  3. def when_done(r):  
  4.     result.append(r.result())  
  5. with ThreadPoolExecutor() as pool:  
  6.     for keep_stock_threshold, buy_change_threshold in \  
  7.             itertools.product(keep_stock_list, buy_change_list):  
  8.         future_result = pool.submit(calc, keep_stock_threshold,  
  9.                                     buy_change_threshold)  
  10.         future_result.add_done_callback(when_done) 

对结果进行排序:

  1. sorted(result)[::-1][:10] 

输出如下:

  1. [(0.5790000000000001, 28, -0.1),  
  2.  (0.5459999999999999, 28, -0.11),  
  3.  (0.519, 26, -0.1),  
  4.  (0.5019999999999999, 28, -0.05),  
  5.  (0.476, 22, -0.09),  
  6.  (0.44800000000000006, 28, -0.07),  
  7.  (0.437, 28, -0.13),  
  8.  (0.437, 28, -0.14),  
  9.  (0.437, 28, -0.15),  
  10.  (0.42500000000000016, 16, -0.15)] 

仔细观察上面多线程运行的输出结果可以发现,与串行的结果和多进程的结果不一致。原因就在clac()函数中:

  1. TradeStrategy2.set_keep_stock_threshold(keep_stock_threshold)  
  2. TradeStrategy2.set_buy_change_threshold(buy_change_threshold) 

这两个设置参数的方法都是类方法,非实例方法。在同一进程中的多个线程不断针对类变量设置参数,结果是错误的,并无我们预想的结果。

在量化中一定要反复推敲验证自己写的每一行代码是否正确,差之毫厘,结果往往是谬以千里。

3.上下文管理器

前面对多进程以及多线程的示例中都使用了形如:

  1. with A_Class() as a:  
  2.     do something 

这样的代码块,使用with作为关键字开头在Python中称为上下文管理器,它的特点是:

在进入上下文管理器定义的缩进模块后,会触发A_Class中定义的__enter__()函数;

在结束上下文管理器定义的缩进模块后,会触发A_Class中定义的__exit__()函数。

一般,在__enter__()和__exit__()函数中定义相反的操作,如文件的打开、关闭,资源的创建和释放等。例如,在线程锁类threading.RLock中可以找到如下实现:

  1. def __enter__(self)  
  2.     self.acquire()  
  3. def __exit__(self, t, v, tb):  
  4.     self.release() 

通过在__enter__()函数中使用acquire()函数来上锁,通过在__exit__()函数中使用release()函数来解锁。

喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

JSP应用开发详解(第三版)

本书结合JSP和Servlet的最新规范,从基本的语法和规范入手,以经验为后盾,以实用为目标,以实例为导向,以实践为指导,深入浅出地讲解了JS...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊