python多线程处理数据并获得子线程返回值

前言

python中采用多线程方式处理大数据量的数据是比较常见和便捷的方法,而有时还想获取每个子线程运行得到的结果,因此将多线程处理和获取子线程返回值的方法做一总结。


python3中的多线程

两种方式:用方法包装函数、用类包装函数。

用方法包装函数

_thread.start_new_thread (func, args[, kwargs])

其中,func是线程函数,args是传递给线程函数的参数且类型必须是tuple,kwargs是可选参数。

注意:

  1. 在python2中,多线程函数模块为thread,但是在python3中该模块已被_thread继承,并重新定义了新的多线程模块threading
  2. _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.")

注意:

  1. 该方法因为需要while 1循环运行才能跑完多个线程,且无法自动退出,只能手动退出;
  2. 由于while 1操作极耗内存,因此需加入sleep机制缓解此情况;
  3. 考虑到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。

获取子线程返回值

获取子线程结果的方法不少,包括:

  1. 从类中返回值;
  2. 设置全局队列写入返回值;
  3. 通过multiprocessing.pool.ThreadPool获取返回值;
  4. 通过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来实现获取子线程结果的功能。

这里需要注意两点:

  1. 一定要join子线程再get_result获取子线程结果,否则只会返回最后一个运行的子线程的结果。这是因为加入join阻塞后主线程必须等待所有子线程执行结束才会结束,那么每个子线程的结果都会通过task.get_result()获得。
  2. 一定不能join加入MyThreadget_result中,这样虽然同样会获取子线程结果,但是多线程执行会失效。这一操作实质上是将多个线程顺序执行,因为在子线程中加入join阻塞后必须等待此子线程执行完才会执行下一个子线程,因此这一操作相当于将程序又变成单线程执行。

其他方法

其他方法暂时还没有研究和实操,等之后试过再写吧。

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值