新增 Thread/Process

leo 2024-04-19 22:09:11 +08:00
parent b3efb36a9b
commit e657e0465e

282
Thread%2FProcess.md Normal file

@ -0,0 +1,282 @@
# 多工
### 用到多工場合:
* 同時間須顯示許多資料
* 讀取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),則沒有辦法獲取資料