進程單例運行,文件加鎖


前言

很多時候我們都需要進程單例運行,當再次運行程序時檢查到已有程序在運行可以做特別的操作,比如置頂已運行的程序,比如當前程序提示一下就退出。
最簡單方案是打開進程創建一個文件,程序結束時刪除文件,當第二個程序運行時判斷該文件存在則認為已有程序運行。問題是程序異常退出沒有刪除那個文件就GG了。
還有方案就是進程啟動時鎖住一個文件,進程退出釋放鎖,進程異常退出由系統自動釋放鎖。這個就是完美的方案。
還有一種方案就是判斷進程名是否允許,Linux下可以執行【ps auxf | grep xxx】,Windows下可以執行【tasklist | findstr xxx】。問題就是程序文件名被改就GG。
另外還可以監聽一個tcp端口,如果第二個進程運行也監聽相同端口會報錯,因此也能實現進程單例運行,只是需要占用一個端口。
在win平台下還可以通過創建一個互斥體實現進程單例。

flock命令

命令介紹

該工具是用來獲取一個文件鎖,並執行命令。當文件已經被鎖則不會執行命令,使用該命令可有效防止重復執行一些操作。特別是cron腳本,由於腳本執行周期長,下一次定時執行又到了,可以不重復執行。
Windows下也是可以安裝flock命令的,我是安裝【msys2】后默認就有了。下面看下該工具的幫助文檔:

flock -h

Usage:
 flock [options] <file>|<directory> <command> [<argument>...]
 flock [options] <file>|<directory> -c <command>
 flock [options] <file descriptor number>

Manage file locks from shell scripts.

Options:
 -s, --shared             get a shared lock                  // 獲取讀鎖(共享鎖),多個進程可獲取並讀文件,其他進程獲取寫鎖會返回失敗
 -x, --exclusive          get an exclusive lock (default)    // 獲取寫鎖(排它鎖),只允許一個進程獲取,其他進程獲取均返回失敗
 -u, --unlock             remove a lock
 -n, --nonblock           fail rather than wait              // 當獲取鎖失敗時直接返回,不帶此選項程序會卡住,直到獲取到鎖
 -w, --timeout <secs>     wait for a limited amount of time  // 獲取鎖超時的時間
 -E, --conflict-exit-code <number>  exit code after conflict or timeout  // 當獲取鎖失敗時,返回的錯誤碼.在Linux執行【echo $?】,win執行【echo %errorlevel%】查看
 -o, --close              close file descriptor before running command
 -c, --command <command>  run a single command string through the shell  // 鎖住文件的同時執行一個命令,我一般用來執行一個腳本
     --verbose            increase verbosity

 -h, --help     display this help and exit
 -V, --version  output version information and exit

For more details see flock(1).

flock實例

代碼實現

獲取文件鎖

可以執行go get github.com/jan-bar/golibs,下載我寫好的庫,關於Windows和Linux下獲取文件鎖和釋放文件鎖。
下面的代碼,同時執行2個進程會返回錯誤file is lock,請看示例程序:

package main

import (
    "os"
    "strconv"
    "time"

    "github.com/jan-bar/golibs/filelock"
)

func main() {
    fr, err := filelock.LockOpenFile("a.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666, filelock.WriteLock)
    if err != nil {
        panic(err)
    }
    defer fr.Close()
    fr.File.Write([]byte(strconv.Itoa(os.Getpid())))
    time.Sleep(time.Second * 5)
}

文件鎖判斷單例

可以根據返回錯誤值判斷文件是否被鎖住,如果鎖住就認為已有進程在執行。

package main

import (
    "fmt"

    "github.com/jan-bar/golibs"
)

func main() {
    err := golibs.SingletonFile("a.txt")
    if err == golibs.ErrSingleton {
        fmt.Println("已有進程在運行")
    }
}

tcp端口判斷單例

如果本機指定端口已經被監聽,則證明已有程序在運行。

package main

import (
    "fmt"

    "github.com/jan-bar/golibs"
)

func main() {
    err := golibs.SingletonTcp(8080)
    if err == golibs.ErrSingleton {
        fmt.Println("已有進程在運行")
    }
}

創建互斥體實現單例

原理是使用win32api中的CreateMutexW創建一個互斥體,多個進程都創建同名互斥體時如果已有進程先創建后面的進程會提示已存在。

package main

import (
    "fmt"

    "github.com/jan-bar/golibs"
)

func main() {
    err := golibs.SingletonWin("process")
    fmt.Println(err)
    fmt.Scanln()
}

總結

    進程單例運行在很多場景都是有必要的,上面介紹的幾種方案,我比較喜歡通過加鎖文件來判斷進程是否已經被運行過,因為只占用一個文件。很多工具都是通過鎖文件,並將自己的pid寫入文件,方便其他操作去讀取這個進程的pid,而不用通過ps去查詢,堪稱完美。我只測試了Windows和Linux下的文件鎖功能,Windows是使用win32api去實現,Linux是系統自帶的接口。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM