在邊緣運算閘道器 (EdgeNode) 的開發中,我們設計了許多指示燈與系統狀態來反映機台當下的狀況。例如:
初期開發時,我們很直覺地在各個模組裡直接呼叫 led.turn_on_green() 或是 led.blink_blue()。
然而,隨著系統越來越複雜,多個執行緒 (Thread) 開始並行運作:一個負責監聽 USB 拔插、一個負責與邊緣感測器 (MCU Node) 通訊、一個負責上傳雲端。
很快地,我們遇到了一個靈異現象:「狀態爭奪鬧劇」。
舉例來說,當 MCU Node 下載成功,準備亮起「綠燈」的瞬間,如果剛好遇到網路斷線,上傳模組會立刻觸發「橘燈」。最後燈號就會卡在半綠半橘,或者發生系統以為在待機,但燈號卻閃著錯誤的詭異狀況。
為了解決這個問題,工程師最常做的直覺反應就是:加 Flag (旗標)。
於是,我們的程式碼裡面開始充滿了各種隱性狀態 (Implicit State):
if not is_error and is_sync_done and not is_uploading:
led.turn_on_green()
elif is_uploading and not is_sync_done:
led.blink_blue()
# ... 無止盡的 if-else 地獄
這種做法有幾個致命缺點:
is_sync_done = True 但同時 is_uploading = True?)。is_error == False 準備亮燈時,Thread B 瞬間把 is_error 改成了 True。這時候你的系統狀態就與硬體表現脫節了。為什麼用 if-else 和 Flag 會這麼痛苦?因為這些變數只是「隱性地」拼湊出一個狀態。
在軟體工程中,如果你的系統有特定的「生命週期」或「階段」,你應該使用 有限狀態機 (Finite State Machine, FSM) 來將狀態「顯性化」。
有限狀態機有幾個核心概念:
IDLE, SYNCING, ERROR, SUCCESS)。為了解決這場爭奪鬧劇,我們決定拔除所有散落在各處的 if-else 與 Flag,為 EdgeNode 建立一個集中的狀態管理中心。
我們先把所有可能的情境收斂成幾個絕對互斥的狀態:
from enum import Enum, auto
class SystemState(Enum):
IDLE = auto() # 待機 (等待設備插入)
SYNCING = auto() # 處理中 (下載/上傳資料)
SUCCESS = auto() # 成功 (可拔除)
ERROR = auto() # 發生錯誤
接下來,我們實作一個 StateManager,所有的 Thread 都不能自己去控制硬體,只能對 StateManager 送出「事件 (Event)」。
import threading
class EdgeNodeFSM:
def __init__(self):
self.state = SystemState.IDLE
self._lock = threading.Lock()
def transition(self, new_state):
with self._lock:
# 狀態轉移的防呆邏輯
if self.state == SystemState.ERROR and new_state != SystemState.IDLE:
print("系統已處於錯誤狀態,必須先回到 IDLE!")
return False
print(f"狀態轉移:{self.state.name} -> {new_state.name}")
self.state = new_state
self._apply_hardware_state()
return True
def _apply_hardware_state(self):
# 將硬體行為 (LED) 綁定到唯一狀態
if self.state == SystemState.IDLE:
led.turn_off()
elif self.state == SystemState.SYNCING:
led.blink_blue()
elif self.state == SystemState.SUCCESS:
led.turn_on_green()
elif self.state == SystemState.ERROR:
led.blink_orange()
導入 FSM 後,不管背後有多少個 Thread 在平行運作,它們能做的事情只有呼叫 fsm.transition(SystemState.ERROR)。
threading.Lock(),我們徹底消滅了 Race Condition。SUCCESS 狀態下試圖觸發 SYNCING 時,FSM 可以直接拒絕這個不合法的轉移,保護了系統的完整性。從「隱性的 Flag 地獄」升級到「嚴謹的 FSM 狀態機」,是把邊緣運算閘道器從「能動就好 (Prototype)」推向「商用穩定 (Production)」的關鍵一步。它不僅讓程式碼更優雅,更從架構層面直接免疫了多執行緒的爭奪鬧劇。
在下一篇,我們將邁入最終章節:SRE 穩定度工程。來看看我們在佈署機器到偏遠場域前,還做了哪些「防禦性」的前置準備!