2024-04-19 22:09:56 +08:00
|
|
|
|
# 多工
|
|
|
|
|
### 用到多工場合:
|
|
|
|
|
* 同時間須顯示許多資料
|
|
|
|
|
* 讀取model和AI運算
|
|
|
|
|
* 需要長時間掛在背景執行
|
|
|
|
|
### 線程/進程 差異
|
|
|
|
|
* 多線程(Thread)
|
|
|
|
|
|
|
|
|
|
簡單講:一個人有2隻手,然後可以同時進行
|
|
|
|
|
|
|
|
|
|
* 多進程(Process)
|
|
|
|
|
|
|
|
|
|
簡單講:聯合其他人一起做
|
|
|
|
|
##
|
|
|
|
|
## 多線程
|
|
|
|
|
### 正常使用
|
|
|
|
|
#### 2個線程幾乎同時啟動
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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個線程運行完才會啟動
|
|
|
|
|
##### 失去多線程意義
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
# 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
### 攜帶參數
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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會使用主線程
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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()
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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的線程溝通
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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個進程幾乎同時啟動
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
# 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個進程運行完才會啟動
|
|
|
|
|
#### 失去多進程意義
|
|
|
|
|
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
# 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 攜帶參數
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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的參數,等等會大概說明
|
2024-06-24 12:55:24 +08:00
|
|
|
|
```python
|
2024-04-19 22:09:56 +08:00
|
|
|
|
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),則沒有辦法獲取資料
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|