動態

詳情 返回 返回

併發控制-文件鎖 - 動態 詳情

在以往的項目中,遇到高併發大流量需求做併發控制的時候一般都使用redis分佈式鎖或者mysql加鎖處理高併發情況。最近遇到一個php項目,沒有安裝redis,由於某種原因也不考慮使用mysql加鎖控制併發,所以採用文件鎖的方式控制併發,整理了下代碼

php版本

class FileLock
{
    /** @var string 鎖名稱 唯一性 */
    private string $key;

    /** @var string 鎖文件 */
    private string $file = "";
    /** @var 文件資源 */
    private $fp = null;

    public function __construct(string $key)
    {
        if (empty($key)) {
            throw new \InvalidArgumentException("key 不能為空");
        }
        $this->key = $key;
    }

    /**
     * 加鎖
     */
    public function lock(): bool
    {
        // 文件路徑
        $file = "lock_{$this->key}.txt";
        $this->file = $file;
        $fp = fopen($file, "w+");
        if (is_resource($fp)) {
            $this->fp = $fp;
        } else {
            return false;
        }
        return flock($fp, LOCK_EX);
    }


    public function unlock(): bool
    {
        if (is_resource($this->fp)) {
            return flock($this->fp, LOCK_UN); // 釋放鎖
        }
        return false;
    }

    public function __destruct()
    {
        if (is_resource($this->fp)) {
            @flock($this->fp, LOCK_UN);
        }
        if (is_file($this->file)) {
            @unlink($this->file);
        }
    }

}

php代碼

go版本

同時整理go版本實現

package main

import (
    "fmt"
    "os"
    "syscall"
    "time"
)

// FileLock 實現文件鎖定
type FileLock struct {
    file string
    f    *os.File
    how  int
}

func (l *FileLock) How(how int) {
    l.how = how
}

func New(file string) *FileLock {
    return &FileLock{
        file: file,
        how:  syscall.LOCK_EX | syscall.LOCK_NB, // 默認非阻塞
    }
}

// Lock 加鎖
func (l *FileLock) Lock() error {
    f, err := os.Create(l.file)
    if err != nil {
        return err
    }
    l.f = f
    // syscall.LOCK_NB 非阻塞
    if err := syscall.Flock(int(f.Fd()), l.how); err != nil {
        return err
    }
    return nil
}

// Unlock 釋放鎖
func (l *FileLock) Unlock() error {
    defer l.f.Close()
    if err := syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN); err != nil {
        // 可以增加報警 或者 直接刪除文件
        return err
    }
    return nil
}

func main() {
    work()
}

func work() {
    // 給該方法加鎖
    lockedFile := "/tmp/my_lock.lock"
    flock := New(lockedFile)
    //flock.How(syscall.LOCK_EX)    阻塞
    err := flock.Lock()
    if err != nil {
        // 獲取鎖失敗
        fmt.Println(err.Error())
        return
    }
    defer flock.Unlock()

    fmt.Println("執行業務代碼,模擬長時間")
    time.Sleep(time.Second * 50)
}

go代碼

user avatar huaihuaidehongdou 頭像 tongbo 頭像 kubeexplorer 頭像 yian 頭像 mangrandechangjinglu 頭像 ansurfen 頭像 anonymous_5f6b14f11289a 頭像 vistart 頭像 xiaolanbenlan 頭像 liberhome 頭像 innsane 頭像 daqidexihongshi 頭像
點贊 22 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.