Golang 實現帶過期時間的單機鎖

單機鎖要實現的目標:

  1. 加鎖:會記錄一個鎖的擁有者 owner

  2. 解鎖:只有鎖的擁有者才能解鎖

  3. 如果有設定鎖的超時時間,到時間自動解鎖(避免忘記解鎖)

代碼很簡單,直接貼源碼。相信聰明的你一看就懂

項目地址: https://github.com/gofish2020/expiredlock

package expiredlock

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

/*
	實現一個帶自動過期時間的單機鎖

	只有鎖的擁有者才能解鎖
*/

type ExpiredLocker struct {
	mutex sync.Mutex // 單機鎖

	processMutex sync.Mutex         // 保護下面的字段
	owner        string// 鎖的擁有者(用進程id+協程id作爲擁有者身份標記)
	cancel       context.CancelFunc //取消函數(讓協程停止)
}

func (l *ExpiredLocker) Lock(expireTime time.Duration) {
	// 加鎖
	l.mutex.Lock()

	l.processMutex.Lock()
	defer l.processMutex.Unlock()
	// 加鎖成功的唯一標識
	l.owner = getPidGid()

	if expireTime <= 0 { // 如果沒有過期時間,說明不需要創建帶過期時間的鎖
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), expireTime)
	l.cancel = cancel

	gofunc() {
		<-ctx.Done()
		if ctx.Err() == context.DeadlineExceeded { // 過期取消
			l.unlocker(l.owner)
		}
	}()
}

func (l *ExpiredLocker) UnLock() {
	l.unlocker(getPidGid()) // 用戶主動調用解鎖(監視過期的協程,如果有啓動,需主動取消掉)
}

func (l *ExpiredLocker) unlocker(owner string) {

	l.processMutex.Lock()
	defer l.processMutex.Unlock()
	// 必須是鎖的擁有者
	if l.owner != "" && l.owner == owner {
		l.owner = ""

		if l.cancel != nil { // 說明是帶過期時間的鎖
			l.cancel() // 目的讓協程也停止
			l.cancel = nil
		}
		l.mutex.Unlock() // 解鎖
	}
}

// 進程id + 協程id 作爲唯一標識
func getPidGid() string {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, _ := strconv.ParseUint(string(b), 10, 64)

	return fmt.Sprintf("%d_%d", os.Getpid(), n)
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Z7NTf9HnwmZ3avoGUlRlYA