東莞做網(wǎng)站哪家最好網(wǎng)站模板建站公司
先說結(jié)論
1. golang提供了syscall包來實現(xiàn)文件/目錄的加鎖,解鎖
2. syscall包屬于文件鎖,是比較底層的技術(shù),并不能在所有操作系統(tǒng)上完全實現(xiàn),linux上實現(xiàn)了,windows下面就沒有
3. 加鎖時調(diào)用syscall.Flock(fd,syscall.LOCK_EX),解鎖時調(diào)用syscall.Flock(fd, syscall.LOCK_UN)
4. 加鎖成功后,對加鎖的文件fd進行Close()操作同樣會釋放鎖,切記
代碼實現(xiàn)
鎖的定義如下內(nèi)部兩個變量:文件/目錄的全路徑名,文件對象
// 文件鎖/目錄鎖
type DirLock struct {
?? ?dir ?? ?string?? ??? ?// 文件/目錄的全路徑名
?? ?f ? ?? ?*os.File?? ?// 文件對象
}
加鎖的實現(xiàn)
核心代碼是 syscall.Flock(int(f.Fd()), LOCK_EX|syscall.LOCK_NB),注意其中的標記
LOCK_EX :加鎖標記。只有一個進程能加鎖成功,其他進程再嘗試加鎖時會阻塞,等同于我們常用的寫鎖
LOCK_NB :不阻塞標記。如果其他進程已加鎖成功,自己去嘗試加鎖時就不再阻塞,而是直接返回錯誤
// 加鎖
func (l *DirLock) Lock() error {
?? ?f, err := os.Open(l.dir)
?? ?if err != nil {
?? ??? ?return err
?? ?}
?? ?l.f = f?? ?err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
?? ?if err != nil {
?? ??? ?return err
?? ?}
?? ?return nil
}
解鎖的實現(xiàn)
核心代碼是?syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)
LOCK_UN :解鎖標記。如果自己已經(jīng)加鎖成功,可以用此標記去解鎖
// 解鎖
func (l *DirLock) Unlock() error {
?? ?defer l.f.Close() // 關(guān)閉文件?? ?return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) // LOCK_UN表解鎖
}
實驗
我們建立5個協(xié)程,每秒去嘗試加鎖一次,失敗則1秒后重試,成功則持續(xù)2秒后解鎖
核心代碼如下
?? ?// 5個協(xié)程,都嘗試對目錄加鎖,加鎖失敗的就重試,加鎖成功的2秒后釋放
?? ?for i := 0; i < 5; i++ {
?? ??? ?wg.Add(1)?? ??? ?go func(num int) {
?? ??? ??? ?dirLock := New(dir)
?? ??? ??? ?ticker := time.NewTicker(time.Second) // 定時器每秒嘗試1次
?? ??? ??? ?for {
?? ??? ??? ??? ?select {
?? ??? ??? ??? ?case <-ticker.C:
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?err := dirLock.Lock() // 加鎖嘗試
?? ??? ??? ??? ??? ??? ?if err != nil {
?? ??? ??? ??? ??? ??? ??? ?fmt.Printf("lock dir failed, goroutine num=%d, err=%s \n", num, err.Error())
?? ??? ??? ??? ??? ??? ??? ?continue
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?fmt.Println("lock dir succeed, goroutine num=", num)
?? ??? ??? ??? ??? ??? ?goto end
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?}
?? ??? ??? ?}?? ??? ??? ?end:
?? ??? ??? ?time.Sleep(time.Second*2)
?? ??? ??? ?dirLock.Unlock() // 解鎖
?? ??? ??? ?wg.Done()
?? ??? ?}(i)
?? ?}
?? ?wg.Wait()
實驗結(jié)果如下圖
完整代碼
package main
import ("fmt""os""sync""syscall""time"
)// 目錄鎖
type DirLock struct {dir string // 目錄的全路徑名f *os.File // 文件對象
}func New(dir string) *DirLock {return &DirLock{dir: dir,}
}// 加鎖
func (l *DirLock) Lock() error {f, err := os.Open(l.dir)if err != nil {return err}l.f = ferr = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)if err != nil {return err}return nil
}// 釋放鎖
func (l *DirLock) Unlock() error {defer l.f.Close() // 其實不執(zhí)行Close()也會釋放目錄鎖return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)
}func main() {dir, _ := os.Getwd()wg := sync.WaitGroup{}// 5個協(xié)程,都嘗試對目錄加鎖,加鎖失敗的就重試,加鎖成功的2秒后釋放for i := 0; i < 5; i++ {wg.Add(1)go func(num int) {dirLock := New(dir)ticker := time.NewTicker(time.Second) // 定時器每秒嘗試1次for {select {case <-ticker.C:{err := dirLock.Lock() // 加鎖嘗試if err != nil {fmt.Printf("lock dir failed, goroutine num=%d, err=%s \n", num, err.Error())continue}fmt.Println("lock dir succeed, goroutine num=", num)goto end}}}end:time.Sleep(time.Second*2)dirLock.Unlock() // 解鎖wg.Done()}(i)}wg.Wait()
}