多線程何如獲取返回值


設定一個場景,在用戶了添加多個任務,點擊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()的作用是: 線程同步,即主線程任務結束之后,進入阻塞狀態,一直等待其他的子線程執行結束之后,主線程在終止

    

 

 

 


  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM