[實踐] 單例程式模式
有時候,我們需要限制某個程式只啟動單一例項程式以實現單例模式(如守護程式)。那麼,在UNIX/Linux環境下,有多少種方法可以實現這一需求?它們的可靠性如何?下面是一些總結心得。
[方法一:使用ps命令對當前同名程式計數]
互斥執行最可靠的辦法是使用互斥鎖。但是跨程式使用互斥鎖是個相當麻煩的事情,再者,某些古舊的UNIX上並沒有此類鎖。於是最土的解決方法是使用ps命令列出當前同名命令列條目並計數,如果數值大於1,則表示程式已經啟動過一次,當前程式可以安心退出。
以經典Shell程式設計為例,最常見的對同名命令列條目計數的寫法是:
count=`ps -ef | grep xxxx.sh | grep -v grep | wc -l`
首先使用ps -ef列出所有程式,然後grep出同名命令列條目(即含有“xxxx.sh”字串的條目,預設按行分隔),同時排除掉grep命令自己的條目,最後使用wc -l完成計數。相當直白的程式碼,一目瞭然,對不對?很可惜,這個寫法有非常隱晦的Bug。
Shell通過呼叫fork()來生成子程式,並在子程式中呼叫exec()來替換執行具體命令(如ps和grep命令,程式號不變),從而建立起完整的管道線。Bug產生自a) fork()出的子程式在呼叫exec()之前,ps命令列出的子程式命令列條目與父程式命令列條目相同,同時b) fork()和exec()不是在一次原子呼叫中完成的。在fork()之後、exec()之前,作業系統剛好排程ps程式執行的話,那麼得到的結果很可能包含2~4個含有“xxxx.sh”字串的條目,這將導致count值不準確,最終執行與預期不相符的後續程式碼。
在這裡,分析Bug的要點是,管道線的各個組成命令不是序列建立和執行的,當執行結果依賴於特定的執行順序時,併發或並行執行會產生微妙的競態條件。避免方法也很簡單,只要打破這一依賴即可:
list=`ps -ef`
count=`echo "${list}" | grep xxxx.sh | grep -v grep | wc -l`
在上述程式碼中,ps -ef執行時fork()和exec()早已完成,不存在因子程式未替換執行具體命令所帶來的同名條目,若有,則可以肯定是在其它終端以相同命令列啟動了同一Shell程式所致,從而避免了計數不準確的問題。
(未完待續……)
相關文章
- 你瞭解單例模式的最佳實踐嗎?單例模式
- 《JavaScript設計模式與開發實踐》模式篇(1)—— 單例模式JavaScript設計模式單例
- [C#.NET 拾遺補漏]06:單例模式實佳實踐C#單例模式
- PHP實現單例模式PHP單例模式
- golang實現單例模式Golang單例模式
- Rust實現單例模式Rust單例模式
- 單例模式 – 單例登錄檔與 Spring 實現單例剖析單例模式Spring
- DCL單例模式中的缺陷及單例模式的其他實現單例模式
- JS中的單例模式及單例模式原型類的實現JS單例模式原型
- JS單例模式《JavaScript設計模式與開發實踐》閱讀筆記JS單例JavaScript設計模式筆記
- 單例模式實現對比單例模式
- 單例模式c++實現單例模式C++
- Python中實現單例模式Python單例模式
- 10.31 實驗7:單例模式單例模式
- 設計模式之單例模式(《JavaScript設計模式與開發實踐》讀書筆記)設計模式單例JavaScript筆記
- 【php實現設計模式】之單例模式PHP設計模式單例
- 用Python實現設計模式——單例模式Python設計模式單例
- 單例模式單例模式
- 利用static來實現單例模式單例模式
- 單例模式:5種實現方式單例模式
- 單例模式的各種實現單例模式
- Go 實現常用設計模式(一)單例模式Go設計模式單例
- 簡單的單例模式其實也不簡單單例模式
- 美團一面:會單例模式嗎,寫個單例看看?(8大單例模式實現方式總結)單例模式
- 創造模式 單例模式模式單例
- 建立型模式:單例模式模式單例
- 設計模式(單例模式)設計模式單例
- [設計模式] 單例模式設計模式單例
- 設計模式-單例模式設計模式單例
- 設計模式 —— 單例模式設計模式單例
- 設計模式 單例模式設計模式單例
- 設計模式——單例模式設計模式單例
- 單例模式解析單例模式
- python 單例模式Python單例模式
- java 單例模式Java單例模式
- 單例模式(Singleton)單例模式
- php單例模式PHP單例模式
- 單例模式(3)單例模式
- Java單例模式Java單例模式