Python/Thread/README.md
2024-06-24 12:55:24 +08:00

283 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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