第一版:
我們用一個文件的存在與否標識進程是否正在執行(很明顯每個進程對應一個文件 - PID文件)
package main import ( "fmt" "os" "time" ) func main() { // 1 定義當前進程PID文件 sigfile := "./cli_syncStaffs.pid" _, err := os.Stat(sigfile) if err == nil { //pid文件存在-進程已經存在 fmt.Println("PID file exist.running...") os.Exit(0) } // 2 創建當前進程的pid文件 pidFileHandle, err := os.OpenFile(sigfile, os.O_RDONLY|os.O_CREATE, os.ModePerm) if err != nil { panic(err) } // 執行業務邏輯 fmt.Println(time.Now().Format("2006-01-02 15:04:05")) time.Sleep(25*time.Second) fmt.Println(time.Now().Format("2006-01-02 15:04:05")) // 執行完畢 err= pidFileHandle.Close() if err!=nil { fmt.Println(err) } // 刪除該文件 err = os.Remove(sigfile) if err!=nil { fmt.Println(err) } }
於是,如果一個進程正在執行的時候,另一個進程執行的話會拋出:
但是,當並發量大的時候 ( 下面我們模擬10個並發 )
package main import ( "fmt" "os" "sync" "time" ) var wg sync.WaitGroup func main() { for i :=0 ; i<10 ;i++{ fmt.Println("start ",i) wg.Add(1) go test() //啟動10個goroutine 來計算 } // 阻塞-保證子協程跑完 wg.Wait() } func test(){ defer wg.Done() // 1 定義當前進程PID文件 sigfile := "./cli_syncStaffs.pid" _, err := os.Stat(sigfile) if err == nil { //pid文件存在-進程已經存在 fmt.Println("PID file exist.running...") return } // 2 創建當前進程的pid文件 pidFileHandle, err := os.OpenFile(sigfile, os.O_RDONLY|os.O_CREATE, os.ModePerm) if err != nil { fmt.Println(err) return } // 執行業務邏輯 fmt.Println(time.Now().Format("2006-01-02 15:04:05")) time.Sleep(5*time.Second) fmt.Println(time.Now().Format("2006-01-02 15:04:05")) // 執行完畢 err= pidFileHandle.Close() if err!=nil { fmt.Println(err) return } // 刪除該文件 err = os.Remove(sigfile) if err!=nil { fmt.Println(err) return } }
很明顯正常來說,即使再快,也是有次序的,也就是說,這10個協程之中,只有一個會輸出時間戳(其他都會告知,pid file exist,running...)
但是輸出卻是這樣的
很明顯,
單純依靠文件是否存在並不能保證進程執行的時候其他進程一定無法執行
我們應該給文件上鎖(並發安全),
當文件無法加鎖的時候,就是有進程正在跑
於是改一下程序:
package main import ( "fmt" "os" "sync" "syscall" "time" ) var wg sync.WaitGroup func main() { for i :=0 ; i<10 ;i++{ fmt.Println("start ",i) wg.Add(1) go test() //啟動10個goroutine 來計算 } // 阻塞-保證子協程跑完 wg.Wait() } func test(){ defer wg.Done() // 1 定義當前進程PID文件 sigfile := "./cli_syncStaffs.pid" // 1 獲取當前的pid文件(沒有就自動創建) pidFileHandle, err := os.OpenFile(sigfile, os.O_RDONLY|os.O_CREATE, os.ModePerm) if err != nil { fmt.Println("open fail.") fmt.Println(err) return } defer pidFileHandle.Close() // 2 文件加鎖 err = syscall.Flock(int(pidFileHandle.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) if err != nil { fmt.Println(err) fmt.Println("running...") return } defer syscall.Flock(int(pidFileHandle.Fd()), syscall.LOCK_UN) // 執行業務邏輯 fmt.Println(time.Now().Format("2006-01-02 15:04:05")) time.Sleep(5*time.Second) fmt.Println(time.Now().Format("2006-01-02 15:04:05")) return }
注意:文件加鎖不支持Windows上運行
以上正確輸出:

start 0 start 1 start 2 start 3 start 4 start 5 start 6 start 7 start 8 start 9 resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... resource temporarily unavailable running... 2020-11-20 01:42:05 2020-11-20 01:42:10
參考博客
https://www.cnblogs.com/pingyeaa/p/11418527.html