python中的多线程和多进程
By 青衣极客 Blue Geek In 2019-09-19
在编写python程序时,有时会有一些并行的需求。比如在一次读取100张图片,按顺序一张张读取当然是能够完成的,只是对磁盘的IO操作的速度是比较慢的,如果使用多线程就能显著提高读取的速度。又比如在进行相互独立的100个矩阵运算的时候,如果按顺序执行会发现你的多核cpu根本没用上。这是由于python具有全局解释锁,在一个python进程中,一个时间点只能有一句代码在cpu上执行,这对于多核cpu来说实在是太浪费了。这时要加快计算的解决方案就是多进程。网上提供的一些多线程和多进程的操作虽然可行,但是在是太繁琐。本文介绍一种最简单的方式让你的python程序跑得飞起。
首先导入与python多线程和多进程之间的模块。
import os
import sys
import time
import threading
import multiprocessing
from multiprocessing import Process, Pool, Manager
1. 定义两个函数
我们在处理任务时一般需要两种函数:一种是处理完任务在函数内输出到显示器或者文件中;另一种是将计算结果回传到主线程或者主进程。本文中就以这样的两个函数来演示这两种并行需求。
# 不传回计算结果的函数
def func(i):
# 增加一些计算量
for j in range(10000000):
pass
print(i) # 直接打印到屏幕
# 回传计算结果的函数
def func_data(i, data_dict):
start = time.perf_counter() # 记录时间
# 增加一些计算量
for j in range(10000000):
pass
# 回填数据
data_dict[i] = time.perf_counter() - start
2. 多线程
这里演示一种函数内输出结果的多线程使用方式。从图中输出的乱序可以看出每个线程是在并行执行的。当然还可以继承一个线程类,然后写自己的功能,但是这种方式稍显麻烦。如果业务需求并不需要那样进行的话,还是采用本文所展示的这种多线程的方式更为方便。
thread_list = []
for i in range(8):
args = (i,)
# 创建线程对象
t = threading.Thread(target=func, args=args)
t.start()
thread_list.append(t)
# 等待所有线程执行完成
for t in thread_list:
t.join()
1
0
3
2
6
4
5
7
3. 传回结果的多线程
在多线程回传结果时,切记不要向同一块内存进行写操作。python中的dict类型可以根据key的不同来进行写入,本文就是采用dict这个结构来回传结果的。
thread_list = [] # 用于存放线程对象
data_dict = {} # 用于存放输出结果
for i in range(8):
args = (i, data_dict)
t = threading.Thread(target=func_data, args=args)
t.start()
thread_list.append(t)
# 等待线程运行完成
for t in thread_list:
t.join()
# 打印多线程运行的结果
for k, v in data_dict.items():
print('thread[{}] cost time: {}'.format(k, v))
thread[2] cost time: 1.1766515919999847
thread[0] cost time: 1.4944783349999398
thread[3] cost time: 1.549116024
thread[1] cost time: 1.5989077380000936
thread[4] cost time: 1.5176890849999154
thread[5] cost time: 1.507154589000038
thread[7] cost time: 1.417290749000017
thread[6] cost time: 1.4539079740000034
4. 多进程
线程是进程的组成部分,因此进程所需要消耗的运行资源一般高于线程。所有如果多线程能够达到目的就不要使用多进程。而在python中,如果想利用多核的计算能力来进行加速,那就非用多进程不可。
pool = Pool(processes=8)
for i in range(8):
args = (i,)
# 加入线程池
pool.apply_async(func=func, args=args)
pool.close() # 关闭进程池,不再加入新的进程
pool.join() # 等待所有进程运行完成
1
3
0
2
4
6
5
7
5. 回传结果的多进程
由于进程之间是使用不同的逻辑地址空间的,所以内建dict这种类型无法在进程之间传递数据。而python的muiltiprocessing模块提供了一些可以在进程之间通讯的数据结构,这也为python多进程的使用提供了方便。
pool = Pool(processes=8) # 定义进程池
data_dict = Manager().dict() # 使用进程间通讯的词典类型
for i in range(8):
args = (i, data_dict)
pool.apply_async(func=func_data, args=args)
pool.close()
pool.join()
# 打印所有进程返回的结果
for k, v in data_dict.items():
print('process[{}] cost time: {}'.format(k,v))
process[2] cost time: 0.4158330709999518
process[7] cost time: 0.41665340800000195
process[3] cost time: 0.41713137800002187
process[5] cost time: 0.4183460669999022
process[1] cost time: 0.42069877700009783
process[6] cost time: 0.4205503159998898
process[0] cost time: 0.42447853199996644
process[4] cost time: 0.4262063829999079
关于多线程与多进程的选择也是很多朋友的模糊点。这里只需要记住几个简单的原则就行:
IO密集型而不是计算密集型的,请使用多线程 计算密集型的请使用多进程 利用多核计算能力的使用多进程
CHANGELOG
- 2020-04-07 增加解说文本

COMMENT
博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题。
"BG09-python中的多线程和多进程"