執行緒模型
一個正在執行的 Run 會在框架和執行階段執行緒上執行工作。您的應用程式不需要管理這些執行緒,但它需要確保其自身的推送、拉取、回調和關閉程式碼保持良好狀態。
執行緒統計
對於典型的 Run:
| 執行緒 | 角色 | 所屬者 |
|---|---|---|
| GStreamer 串流執行緒 | 將緩衝區移動到來源、轉換和接收路徑中。 | GStreamer 執行階段 |
| MLA 派遣執行緒 | 提交 MLA 工作並處理完成結果。 | Neat 執行階段 |
| EV74 / CVU 派遣執行緒 | 提交 CVU 端核心並輪詢完成狀態。 | Neat 執行階段 |
| 拉取端等待者和匯流排監視器 | 將輸出樣本移動到公共佇列中,並報告執行階段錯誤。 | Neat 執行階段 |
| 應用程式執行緒 | 呼叫 Graph.build(...)、Graph.run(...)、Run.push(...)、Run.pull(...)、Run.stop() 或回調。 | 您的應用程式 |
Graph 片段、來源節點和接收節點可以新增執行階段工作,但規則不變:公開的 Run 是由應用程式控制的句柄。
應用程式程式碼的執行位置
使用者程式碼出現在以下位置:
- 直接 API 呼叫:
build(...)、run(...)、push(...)、try_push(...)、pull(...)、close_input()、stop()和close()在呼叫它們的應用程式執行緒上執行。 - 關閉回調:
RunOptions.on_input_drop在推送路徑上執行。保持簡短;計算並傳回。 - 張量回調:
Graph::set_tensor_callback(...)是一種 C++ 回調樣式的消耗方式。保持回調簡短,因為長時間的工作會對執行階段路徑產生反壓。 - 自訂節點描述:公共節點建構和圖組成在建構時發生,在應用程式執行緒上。
不要將耗時的操作隱藏在回調中。如果回調需要執行大量工作,請將樣本傳遞到您自己的佇列中,然後傳回。
鎖定規則
除非 API 另有說明,否則將 Graph、Run、Tensor 和 Sample 視為單一所有者物件。
支援的模式:
- 一個生產者執行緒呼叫
push(...)或try_push(...); - 一個消費者執行緒呼叫
pull(...); - 另一個協調執行緒可以在關閉時呼叫
stop()。
有風險的模式:
- 幾個生產者執行緒推送到同一個
Run,而沒有鎖定; - 幾個消費者執行緒從同一個輸出拉取,而沒有明確的擁有權規則;
- 一個回調呼叫到同一個
Run,並等待更多工作。
如果您需要多個生產者,請在呼叫 Run.push(...) 之前序列化訪問。如果您需要多個消費者,請在一個執行緒 從 Neat 拉取後進行分流。
關閉和取消
使用與意圖匹配的關閉原語:
| 意圖 | 使用 |
|---|---|
| 在最後一個輸入之後完成佇列中的工作 | run.close_input(),然後拉取,直到沒有更多輸出或傳回 PullStatus::Closed。 |
| 現在停止並解除阻塞等待中的工作 | run.stop() |
| 釋放執行階段資源 | run.close() 或讓 Run 物件離開作用域 |
stop() 是取消路徑。取消後,正在進行中的讀取操作將會解除阻塞,並且推送操作應該停止。不要繼續將資料推送至即將結束的執行階段。
吞吐量執行緒模型
對於應用程式推送的即時或高吞吐量圖,從兩個應用程式執行緒開始:
- 一個生產者執行緒讀取輸入,加上
stream_id/frame_id,並根據選定的OverflowPolicy呼叫try_push(...)或push(...)。 - 一個消費者執行緒持續讀取,並在資料被固定到由執行階段支援的緩衝區之前釋放或複製輸出。
在您自己的佇列周圍新增更多應用程式執行緒,而不是在同一個 Run 物件周圍新增。讓熱迴圈變得單調。單調意味著快速。