Python/Thread
2024-06-24 12:55:24 +08:00
..
code 新增code 2024-04-19 19:54:41 +08:00
python_thread.pptx upload 2024-04-19 19:24:34 +08:00
README.md 更新 Thread/README.md 2024-06-24 12:55:24 +08:00

多工

用到多工場合:

  • 同時間須顯示許多資料
  • 讀取model和AI運算
  • 需要長時間掛在背景執行

線程/進程 差異

  • 多線程(Thread)

    簡單講:一個人有2隻手然後可以同時進行

  • 多進程(Process)

    簡單講:聯合其他人一起做

多線程

正常使用

2個線程幾乎同時啟動

import threading
from queue import Queue #Thread 無法回傳值,所以要使用 Queue.put() 將要傳回的值存入 Queue再用 Queue.get() 取出
import time
import os
# A報數
def A_Count_off():
    for i in range(0,5):
        print(f'A : {i}')
        time.sleep(0.5)


# B報數
def B_Count_off():
    for i in range(5,10):
        print(f'B : {i}')
        time.sleep(0.5)



thread_list=[A_Count_off,B_Count_off]
thread_num =[]
for i in range(0,len(thread_list)):
    thread_num.append(threading.Thread(target=thread_list[i]))

for i in range(0,len(thread_num)):
    thread_num[i].join()


非正常使用

第2個線程要等到第1個線程運行完才會啟動

失去多線程意義
# A報數
def A_Count_off():
    for i in range(0,5):
        print(f'A : {i}')
        time.sleep(0.5)


# B報數
def B_Count_off():
    for i in range(5,10):
        print(f'B : {i}')
        time.sleep(0.5)



thread_list=[A_Count_off,B_Count_off]
thread_num =[]

for i in range(0,len(thread_num)):
    thread_num[i].start()
    thread_num[i].join()


攜帶參數

def Count_off(code_name):
    print('process {} '.format(os.getpid()))
    print('thread {} '.format(threading.current_thread().name))
    for i in range(0,5):
        print(f'{code_name} : {i}')
        time.sleep(0.5)


thread_name_list=["A","B","C","D"]

thread_list=[]
for i in range(0,len(thread_name_list)):
    thread_list.append(threading.Thread(target=Count_off,args=thread_name_list[i])) # 攜帶參數
for i in range(0,len(thread_list)):
    thread_list[i].start()
for i in range(0,len(thread_list)):
    thread_list[i].join()

使用class的方式(推薦使用)

後續觀看程式碼及維護,會提高不少

  • start會使用分出去的線程
  • run會使用主線程
class Thread_class(threading.Thread):
    def __init__(self,code_name):
        threading.Thread.__init__(self)
        self.code_name = code_name

    def run(self):
        print('process {} '.format(os.getpid()))  # 查看進程
        print('thread {} '.format(threading.current_thread().name))  # 查看線程
        for i in range(0,5):
            print(f'{self.code_name} : {i}')
            time.sleep(0.5)
    def test_return(self):
        return(f'{self.code_name}=END')
thread_name_list=["A","B","C","D"]
thread_list=[]
for i in range(0,len(thread_name_list)):
    thread_list.append(Thread_class(thread_name_list[i]))
for i in range(0,len(thread_list)):
    thread_list[i].start()  # 啟動額外線程
for i in range(0,len(thread_list)):
    thread_list[i].join()
    
class Thread_class(threading.Thread):
    def __init__(self,code_name):
        threading.Thread.__init__(self)
        self.code_name = code_name

    def run(self):
        print('process {} '.format(os.getpid()))  # 查看進程
        print('thread {} '.format(threading.current_thread().name))  # 查看線程
        for i in range(0,5):
            print(f'{self.code_name} : {i}')
            time.sleep(0.5)
    def test_return(self):
        return(f'{self.code_name}=END')
thread_name_list=["A","B","C","D"]
thread_list=[]
for i in range(0,len(thread_name_list)):
    thread_list.append(Thread_class(thread_name_list[i]))
for i in range(0,len(thread_list)):
    thread_list[i].run()  # 這樣只會套用到 呼叫method,會用主線程去調用
for i in range(0,len(thread_list)):
    thread_list[i].join()

Qthread:

  • 使用方式與thread相似
  • 多了信號槽使用(可以擁有多個)
  • 方便與主UI的線程溝通
class ReadTime(QtCore.QThread):  # 讀取時間
    time_out = pyqtSignal(str)  # 聲明一個帶字串參數的信號槽
    def __init__(self, parent=None):
        super().__init__(parent)
    def run(self):
        while True:
            result = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())  # 讀取當下時間
            self.time_out.emit(f'{result}')  # 傳送信号
            self.msleep(500)  # 休眠0.5秒

多進程

多進程與多線程的寫法極為相似

正常使用:

2個進程幾乎同時啟動

# A報數
def A_Count_off():
    for i in range(0,5):
        print(f'A : {i}')
        time.sleep(0.5)


# B報數
def B_Count_off():
    for i in range(5,10):
        print(f'B : {i}')
        time.sleep(0.5)


if __name__=='__main__':
    process_list=[A_Count_off,B_Count_off]
    process_name =[]
    for i in range(0,len(process_list)):
        process_name.append(mp.Process(target=process_list[i]))
        
    for i in range(0,len(process_name)):
        process_name[i].start()
        
    for i in range(0,len(process_name)):
        process_name[i].join()

非正常使用:

第2個進程要等到第1個進程運行完才會啟動

失去多進程意義

# A報數
def A_Count_off():
    for i in range(0,5):
        print(f'A : {i}')
        time.sleep(0.5)


# B報數
def B_Count_off():
    for i in range(5,10):
        print(f'B : {i}')
        time.sleep(0.5)


if __name__=='__main__':
    process_list=[A_Count_off,B_Count_off]
    process_name =[]
    for i in range(0,len(process_list)):
        process_name.append(mp.Process(target=process_list[i]))
        
    for i in range(0,len(process_name)):
        process_name[i].start()
        process_name[i].join()
        

攜帶參數

if __name__=='__main__':
    process_name_list=["A","B","C","D"]
    process_list=[]
    for i in range(0,len(process_name_list)):
        process_list.append(mp.Process(target=Count_off,args=process_name_list[i])) # 攜帶參數

使用class的方式(推薦使用)

後續觀看程式碼及維護,會提高不少

  • start會使用分出去的進程
  • run會使用主線程
  • 但start後無法直接調用此class的參數等等會大概說明
class Process_class(mp.Process):
    def __init__(self, code_name):
        mp.Process.__init__(self)
        self.code_name = code_name

    def run(self):
        print('process {} '.format(os.getpid())) # 查看進程
        print('thread {} '.format(threading.current_thread().name)) # 查看線程
        for i in range(0,5):
            print(f'{self.code_name} : {i}')
            time.sleep(0.5)
    def test_return(self):
        return(f'{self.code_name}=END')

if __name__=='__main__':
    start_time = time.time()
    process_name_list = ["A", "B", "C", "D"]
    process_list = []
    for i in range(0, len(process_name_list)):
        process_list.append(Process_class(process_name_list[i]))
    for i in range(0, len(process_list)):
        process_list[i].start()
    for i in range(0, len(process_list)):
        process_list[i].join()

    for i in range(0, len(process_list)):
        print(process_list[i].test_return())

多進程的溝通方式

多進程的溝通方式需要透過Queue或pipe

參考資料
http://www.taroballz.com/2018/01/11/processing_communcation/
多線程由於共享記憶體溝通方式比多進程方便
2者差異的參考資料
https://blog.csdn.net/Victor2code/article/details/109005171

用團隊合作方式來理解,每個人都在做自己的事,若沒有溝通或是告知(Queue),則沒有辦法獲取資料