1 回答

TA貢獻1802條經驗 獲得超6個贊
好的,現在清楚多了。好吧,理想情況下,您應該從一些有關 Windows 服務的構成的教程開始 - 我打賭這可能已經為您解決了問題。但無論如何我們還是要嘗試一下。
一些理論
Windows 服務有兩個方面:它執行一些有用的任務并與SCM 設施進行通信。當您使用命令或通過控制面板操作服務時sc
,您可以使用該軟件代表您與 SCM 進行對話,而 SCM 則與該服務進行對話。
SCM 和服務使用的確切協議是低級且復雜的,您使用的 Go 包的目的是向您隱藏這種復雜性,并為這些東西提供一個合理的以 Go 為中心的接口。
正如您可能從自己的示例中了解到的那樣,Execute
您創建的類型的方法在很大程度上與與 SCM 的通信有關:它運行一個無限for
循環,在每次迭代時都會休眠從r
通道讀取數據,并且該通道將 SCM 命令傳送到您的服務。
所以你基本上擁有了所謂的“SCM 命令處理循環”。
現在回想一下上面的兩個方面。您已經擁有其中之一:您的服務與 SCM 交互,因此您需要另一個 — 實際執行有用任務的代碼。
事實上,它已經部分存在:您獲取的示例代碼創建了一個時間行情指示器,它提供了一個通道,當另一個時間行情通過時,它在該通道上傳遞值。for
該方法中的循環也Execute
從該通道讀取數據,每次發出另一個刻度信號時都會“工作”。
好吧,這對于一個玩具示例來說很好,但對于真正的作品來說卻很糟糕。
接近解決方案
因此,讓我們暫停一下并考慮一下我們的需求。
我們需要一些代碼來運行并執行我們的實際任務。
我們需要現有的命令處理循環才能繼續工作。
我們需要這兩段代碼同時工作。
在這個玩具示例中,第三點是“免費”的,因為時間計時器自動執行等待下一個計時的任務,并且與其余代碼完全并發。
你真正的代碼很可能不會有那么奢侈,那么你該怎么辦?
在 Go 中,當你需要同時做某件事和其他事情時,一個明顯的答案是“使用 goroutine”。
因此,第一步是獲取現有代碼,將其轉換為可調用函數,然后在進入循環之前在單獨的 goroutine 中調用它for
。這樣,您將同時運行兩個部分。
困難的部分
好吧,那并不難。
困難的部分是:
如何配置執行任務的代碼。
如何使單片機命令處理循環和執行任務的代碼進行通信。
配置
這實際上取決于您的 $dayjob 或 $current_project 的政策,但有一些提示:
Windows 服務可以接收命令行參數——無論是單次運行還是永久(在每次運行時傳遞給服務)。
缺點是從 UI/UX 的角度來看使用它們并不方便。
通常 Windows 服務用于讀取注冊表。
如今(在 .NET 及其普遍的 xml-ity 出現之后)服務傾向于讀取配置文件。
大多數時候,操作系統環境并不適合該任務。
您可以合并其中幾個場地。
我想我會從配置文件開始,但話又說回來,我認為你應該選擇阻力最小的路徑。
需要記住的一件事是,配置的讀取和處理最好在服務向 SCM 發出它已啟動的信號之前完成:如果配置無效或無法加載,服務應廣泛記錄該情況并發出信號失敗,并且不運行實際的任務處理代碼。
命令處理循環和攜帶代碼的任務之間的通信
在我看來,這是最難的部分。
在這里寫一整本書是可能的,但現在讓我們保持簡單。
為了使其盡可能簡單,我會執行以下操作:
考慮暫停、停止和關閉幾乎相同:所有這些信號都必須告訴您的任務處理代碼退出,然后等待它實際執行此操作。
將“繼續”信號視為與啟動任務處理函數相同:在新的 goroutine 中再次運行它。
進行單向通信:從控制循環到任務處理代碼,而不是其他方式 - 這將大大簡化服務狀態管理。
這樣,您可以創建一個任務處理代碼偵聽或定期檢查的通道,當一個值來自該通道時,代碼停止運行,關閉通道并退出。
當 SCM 告訴控制循環暫停、停止或關閉時,控制循環會在該通道上發送任何內容,然后等待其關閉。當發生這種情況時,它知道任務處理代碼已完成。
在 Go 中,僅用于信令的通道的范例是具有類型struct{}
(空struct
)的通道。
如何在運行代碼的任務中監視此控制通道的問題是一個開放的問題,并且在很大程度上取決于它執行的任務的性質。
這里的任何進一步幫助都可以背誦 Go 書籍中有關并發的內容,因此您應該首先了解這一點。
還有一個有趣的問題是如何使控制循環和任務處理循環之間的通信能夠適應后者可能出現的處理停頓,但話又說回來,在我看來,現在討論這個問題還為時過早。
- 1 回答
- 0 關注
- 205 瀏覽
添加回答
舉報