golang并发编程-05-同步-01-锁的使用(sync包Mutex、RWMutex结构体的常用方法)

@

1. 互斥锁

1.1. 结构体 Mutex

1.1.1 结构体定义

type Mutex struct {
    state int32
    sema  uint32
}

1.1.2 结构体下方法

  • Lock()
  • lockSlow()
  • Unlock()
  • unlockSlow(new int32)

1.2 Lock()/Unlock()方法

1.2.1 语法

  • 获取锁
func (m *Mutex) Lock()
  • 释放锁
func (m *Mutex) Unlock()
  • 示例
import (
	"sync"
)

func main() {
	var mutex sync.Mutex
	func() {
		mutex.Lock()
		defer mutex.Unlock()
	}()
}

2.2.2 示例(赤壁之战——锁的争夺)

说明:魏蜀吴三个协程争夺锁,一个退出后,另一个才能获得锁。

func main() {
	var mutex sync.Mutex
	fmt.Println("======长江天险构造完成=======")
	for _,i := range []string{"刘备","曹操","孙权"} {
		go func(i string) {
			fmt.Printf("【%s】 出兵 \n", i)
			mutex.Lock()
			defer mutex.Unlock()
			fmt.Printf("【%s】 +++++占据长江+++++ \n", i)
			time.Sleep(3*time.Second)
			fmt.Printf("【%s】 退出长江 ====》 \n", i)
		}(i)
	}
	time.Sleep(10*time.Second)
	fmt.Println("=====END 折戟沉沙=====")
}

结果输出

======长江天险构造完成=======
【孙权】 出兵 
【孙权】 +++++占据长江+++++ 
【刘备】 出兵 
【曹操】 出兵 
【孙权】 退出长江 ====》 
【刘备】 +++++占据长江+++++ 
【刘备】 退出长江 ====》 
【曹操】 +++++占据长江+++++ 
【曹操】 退出长江 ====》 
=====END 折戟沉沙=====

2. 读写锁

特点

  • 许任意个读操作同时进行
  • 同一时刻,只允许有一个写操作

2.1 结构体 RWMutex

结构体

type RWMutex struct {
    w           Mutex
    writerSem   uint32
    readerSem   uint32
    readerCount int32
    readerWait  int32
}

该结构体方法

  • RLock()
  • RUnlock()
  • rUnlockSlow(r int32)
  • Lock()
  • Unlock()
  • RLocker() sync.Locker

2.2 示例(献帝驾到——读锁间不互斥)

如下,三个协程使用读锁同时进行。

func main() {
	var rwLock sync.RWMutex
	var wg sync.WaitGroup
	wg.Add(3)
	fmt.Println("====== 献帝驾到 =======")
	for _,i := range []string{"刘备","曹操","孙权"} {
		go func(i string) {
			rwLock.RLock()
			defer rwLock.RUnlock()
			fmt.Printf("【%s】 觐见 +++++ \n", i)
			time.Sleep(3*time.Second)
			fmt.Printf("【%s】 退出 ====》\n", i)
			wg.Done()
		}(i)
	}
	wg.Wait()
	fmt.Println("=====END=====")
}

结果

====== 献帝驾到 =======
【孙权】 觐见 +++++ 
【刘备】 觐见 +++++ 
【曹操】 觐见 +++++ 
【曹操】 退出 ====》
【孙权】 退出 ====》
【刘备】 退出 ====》
===== END =====

2.3 示例(衣带诏——读写锁是互斥的)

读锁和写锁是互斥的

  • 读锁锁定后,写锁的线程等待。
  • 写锁锁定后,其他写锁和读锁线程都等待。

示例

献帝密诏董承,传衣带诏。其他人不能在此时觐见。
(其他人除了曹操我都是瞎写的)

func main() {
	var rwLock sync.RWMutex
	var wg sync.WaitGroup
	wg.Add(5)
	fmt.Println("====== 献帝驾到 =======")

	go func() {
		rwLock.Lock()
		defer rwLock.Unlock()
		fmt.Println("【董承】 被密诏 !!!!")
		time.Sleep(3*time.Second)
		fmt.Println("【董承】 退出 ====》")
		wg.Done()
	}()
	for _,i := range []string{"曹操","孙权","袁绍","马腾"} {
		go func(i string) {
			rwLock.RLock()
			defer rwLock.RUnlock()
			fmt.Printf("【%s】 觐见 +++++ \n", i)
			time.Sleep(3*time.Second)
			fmt.Printf("【%s】 退出 ====》\n", i)
			wg.Done()
		}(i)
	}
	wg.Wait()
	fmt.Println("=====END=====")
}

结果(因为每个人是一个协程,所以每次打印结果顺序不同)

====== 献帝驾到 =======
【马腾】 觐见 +++++ 
【马腾】 退出 ====》
【董承】 被密诏 !!!!
【董承】 退出 ====》
【袁绍】 觐见 +++++ 
【曹操】 觐见 +++++ 
【孙权】 觐见 +++++ 
【曹操】 退出 ====》
【孙权】 退出 ====》
【袁绍】 退出 ====》
=====END=====

如上:

  • 马腾先到,觐见是读锁。
  • 董承第二个到,密诏是写锁,董承等待读锁解锁后进入。
  • 之后,曹操、孙权、袁绍依次到达,觐见是读锁,不会互斥,一起进入。

2.4 示例3(三顾茅庐——读锁是可重入锁)

一个协程可多次获取读锁(在该读锁解锁前)

  • 示例

刘备可以多次面见诸葛亮,获得诸葛亮的读锁,而不必先解锁。

func main() {
	var rwLock sync.RWMutex
	var wg sync.WaitGroup
	wg.Add(3)
	fmt.Println("====== 三顾茅庐 =======")

	go func() {
		for i:=1;i<=3;i++{
			rwLock.RLock()
			fmt.Printf("【刘备】第 %d 次拜访【诸葛亮】 \n", i)
			time.Sleep(time.Second)
		}
		for j:=1;j<=3;j++{
			rwLock.RUnlock()
			wg.Done()
		}
	}()
	wg.Wait()
	fmt.Println("=====END=====")
}

结果:

====== 三顾茅庐 =======
【刘备】第 1 次拜访【诸葛亮】 
【刘备】第 2 次拜访【诸葛亮】 
【刘备】第 3 次拜访【诸葛亮】 
=====END=====

posted on 2022-05-09 20:56  运维开发玄德公  阅读(12)  评论(0编辑  收藏  举报  来源

导航