前言
python中采用多线程方式处理大数据量的数据是比较常见和便捷的方法,而有时还想获取每个子线程运行得到的结果,因此将多线程处理和获取子线程返回值的方法做一总结。
python3中的多线程
两种方式:用方法包装函数、用类包装函数。
用方法包装函数
_thread.start_new_thread (func, args[, kwargs])
其中,func是线程函数,args是传递给线程函数的参数且类型必须是tuple,kwargs是可选参数。
注意:
- 在python2中,多线程函数模块为
thread
,但是在python3中该模块已被_thread
继承,并重新定义了新的多线程模块threading
。_thread
模块提供了低级别的、原始的线程以及一个简单的锁,它相比于threading
模块的功能比较有限。
以下是一个通过_thread
模块实现多线程的简单示例:
import _thread
import time
def print_time(thread_name, delay):
c = 0
while c < 5:
time.sleep(delay)
c += 1
res = "%s: %s" % (thread_name, time.ctime(time.time()))
print(res)
# return res # 作为线程运行时函数中的return是无意义的
if __name__ == "__main__":
# 用方法包装线程
try:
try:
_thread.start_new_thread(print_time, ("Thread-1", 1, ))
_thread.start_new_thread(print_time, ("Thread-2", 2, ))
except:
pass
# 谨慎使用此操作:极其耗费cpu
while 1:
time.sleep(0.1)
except KeyboardInterrupt:
print("You stop the threading.")
注意:
- 该方法因为需要
while 1
循环运行才能跑完多个线程,且无法自动退出,只能手动退出;- 由于
while 1
操作极耗内存,因此需加入sleep
机制缓解此情况;- 考虑到
threading
模块构建类进行多线程操作更友好,因此实际应用中不采用此方法。
用类包装函数
首先通过threading
模块构建多线程类MyThread.py。
import threading
import time
# MyThread.py线程类
class MyThread(threading.Thread):
def __init__(self, func, args=()):
super(MyThread, self).__init__()
self.func = func
self.args = args
def run(self):
time.sleep(1)
self.func(*self.args)
其中,func是线程函数,arg是传递给线程函数的参数且类型是tuple。
获取子线程返回值
获取子线程结果的方法不少,包括:
- 从类中返回值;
- 设置全局队列写入返回值;
- 通过multiprocessing.pool.ThreadPool获取返回值;
- 通过concurrent.futures,结合set_result和result方法获取返回值等。
从类中返回值
从类中返回值是非常常见简便的方法,该方法的多线程类可以将上文中的MyThread.py文件稍作修改:
import threading
import time
# MyThread.py线程类
class MyThread(threading.Thread):
def __init__(self, func, args=()):
super(MyThread, self).__init__()
self.func = func
self.args = args
self.result = []
def run(self):
time.sleep(1)
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception:
return None
在该方法的基础上,就可以实现返回子线程结果的功能了。
以下通过一个简单示例来说明:
from MyThread import MyThread
def add(a, b):
c = a + b
return c
if __name__ == "__main__":
# 用类包装线程
ls = [23, 89]
thr = []
# 创建4个线程
for i in range(4):
task = MyThread(add, (ls[0], ls[1]))
task.start()
task.join()
thr.append(task.get_result())
print(thr)
这里写了一个简单的加法函数add,通过调用多线程类MyThread来实现获取子线程结果的功能。
这里需要注意两点:
- 一定要先
join
子线程再get_result
获取子线程结果,否则只会返回最后一个运行的子线程的结果。这是因为加入join
阻塞后主线程必须等待所有子线程执行结束才会结束,那么每个子线程的结果都会通过task.get_result()
获得。- 一定不能将
join
加入MyThread的get_result
中,这样虽然同样会获取子线程结果,但是多线程执行会失效。这一操作实质上是将多个线程顺序执行,因为在子线程中加入join
阻塞后必须等待此子线程执行完才会执行下一个子线程,因此这一操作相当于将程序又变成单线程执行。
其他方法
其他方法暂时还没有研究和实操,等之后试过再写吧。