多线程何如获取返回值

设定一个场景,在用户了添加多个任务,点击run task按钮在后台处理这些tasks,并判断task成功或失败,因为task是耗时的,所以采用多线程方式处理tasks

考虑:

  线程启动后如何获取task执行结果?

  看代码:

  

import threading
import time


class TaskThread(threading.Thread):
    """
    处理task相关的线程类
    """

    def __init__(self, func, args=()):
        super(TaskThread, self).__init__()
        self.func = func  # 要执行的task类型
        self.args = args  # 要传入的参数

    def run(self):
        # 线程类实例调用start()方法将执行run()方法,这里定义具体要做的异步任务
        print("start func {}".format(self.func.__name__))  # 打印task名字 用方法名.__name__
        self.result = self.func(*self.args)  # 将任务执行结果赋值给self.result变量

    def get_result(self):
        # 改方法返回task函数的执行结果,方法名不是非要get_result
        try:
            return self.result
        except Exception as ex:
            print(ex)
            return "ERROR"


def task_type1(task_id, task_name):
    print("start tasks, name:{}, id:{}".format(task_name, task_id))
    time.sleep(2)
    print("end tasks, name:{}, id:{}".format(task_name, task_id))
    return task_id


thread_pool = []  # 列表用来保存线程实例
for i in range(10):
    # 循环创建线程对象
    thread = TaskThread(task_type1, args=(i + 1, 'pay'))
    # 将线程对象添加到pool
    thread_pool.append(thread)
    # 起动线程 执行tasks
    thread.start()

for thread in thread_pool:
    # 重要的一步,为什么一定要join
    thread.join()
    # 从线程pool中获取结果
    print("result:{}".format(thread.get_result()))

 

 

运行结果:

  

start func task_type1
start tasks, name:pay, id:1
start func task_type1
start tasks, name:pay, id:2
start func task_type1
start tasks, name:pay, id:3
start func task_type1
start tasks, name:pay, id:4
start func task_type1
start tasks, name:pay, id:5
start func task_type1
start tasks, name:pay, id:6
start func task_type1
start tasks, name:pay, id:7
start func task_type1
start tasks, name:pay, id:8
start func task_type1
start tasks, name:pay, id:9
start func task_type1
start tasks, name:pay, id:10
end tasks, name:pay, id:4
end tasks, name:pay, id:2
end tasks, name:pay, id:1
end tasks, name:pay, id:5
end tasks, name:pay, id:8
end tasks, name:pay, id:3
result:1
result:2
end tasks, name:pay, id:9
result:3
result:4
result:5
end tasks, name:pay, id:10
end tasks, name:pay, id:6
result:6
end tasks, name:pay, id:7
result:7
result:8
result:9
result:10

 

上面代码实现了创建线程执行task,并获取任务,关键点在于线程类中实现了ge_result方法,用来获取任务函数的返回值,并且用到了thread.join(),这是必须的,如果没有thread.join()将会怎么样呢?

注释掉:thread.join() 并重新运行代码:

  

start func task_type1
start tasks, name:pay, id:1
start func task_type1
start tasks, name:pay, id:2
start func task_type1
start tasks, name:pay, id:3
start func task_type1
start tasks, name:pay, id:4
start func task_type1
start tasks, name:pay, id:5
start func task_type1
start tasks, name:pay, id:6
start func task_type1
start tasks, name:pay, id:7
start func task_type1
start tasks, name:pay, id:8
start func task_type1
start tasks, name:pay, id:9
start func task_type1
start tasks, name:pay, id:10
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
'TaskThread' object has no attribute 'result'
result:ERROR
end tasks, name:pay, id:1
end tasks, name:pay, id:3
end tasks, name:pay, id:2
end tasks, name:pay, id:5
end tasks, name:pay, id:6
end tasks, name:pay, id:7
end tasks, name:pay, id:4
end tasks, name:pay, id:8
end tasks, name:pay, id:9
end tasks, name:pay, id:10

如果没有join,我们得到了这样的结果,这是为什么呢?
  这是因为self.result在run方法中,只有self.func执行结束后,self.resultb才被赋值,我们在调用get_result时,run方法并未执行结束,self.result自然也未被赋值,所以抛了'TaskThread' object has no attribute 'result'异常.

那为什么join以后就可以正常获取self.result呢?
  这就要理解一下多线程的原理:
  1.当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务

  2. 如果setDaemon(True)方法,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,可能出现的情况就是,子线程的任务还没有完全执行结束,就*停止,当然,这不是我们想要看到的
  
  3. 我们希望看到的结果是当主线程结束后阻塞,等待子线程执行完成,这时需要用到join()


那能将join()放在start()后面吗?
  试一下:
  thread.start()
  
  thread.join()

  执行结果:
  
start func task_type1
start tasks, name:pay, id:1
end tasks, name:pay, id:1
start func task_type1
start tasks, name:pay, id:2
end tasks, name:pay, id:2
start func task_type1
start tasks, name:pay, id:3
end tasks, name:pay, id:3
start func task_type1
start tasks, name:pay, id:4
end tasks, name:pay, id:4
start func task_type1
start tasks, name:pay, id:5
end tasks, name:pay, id:5
start func task_type1
start tasks, name:pay, id:6
end tasks, name:pay, id:6
start func task_type1
start tasks, name:pay, id:7
end tasks, name:pay, id:7
start func task_type1
start tasks, name:pay, id:8
end tasks, name:pay, id:8
start func task_type1
start tasks, name:pay, id:9
end tasks, name:pay, id:9
start func task_type1
start tasks, name:pay, id:10
end tasks, name:pay, id:10
result:1
result:2
result:3
result:4
result:5
result:6
result:7
result:8
result:9
result:10

  结果是拿到了,但是每次start()之后join,则子线程阻塞,知道执行结束才开始下一次循环,这样的执行效率等同于单线程循环,如果一个线程任务执行2秒,那这样的方式执行10个任务就要20s,显然是错误的用法,因为join()的作用是: 线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止

    

 

 

 


  

 

上一篇:Linux和Windows的遍历目录下所有文件的方法对比


下一篇:c# 红袖支付(速邦,随行付)接口