缓存模块之并发编程-sync.Muxtex入门和double-check

第九周_缓存模块之并发编程_sync.Mutex入门和double-check

sync包——Mutex和RWMutex

Mutex可以看做是锁,而RWMutex则是读写锁。一般的用法是将 Mutex 或者 RWMutex 和需要被保住的资源封装在一个结构体内。

  • 如果有多个 goroutine 同时读写的资源,就一定要保护起来。
  • 如果多个 goroutine 只读某个资源,那就不需要保护。

使用锁的时候,优先使用 RWMutex。

  • RWMutex:核心就是四个方法,RLock、RUnlock、Lock、Unlock
  • Mutex:Lock 和 Unlock

举例

SafeMap 的 LoadOrStore 写法

例子:SafeMap 可以看做是 map 的一个线程安全的封装。我们为它增加一个 LoadOrStore 的方法。

// safe_map.go
package sync

import "sync"

type SafeMap[K comparable, V any] struct {
	data  map[K]V
	mutex sync.RWMutex
}

func (s *SafeMap[K, V]) Put(key K, val V) {
	s.mutex.Lock()
	defer s.mutex.Unlock() // 为什么在这调用defer解锁,是因为下一行代码如果发生panic,函数退出会调用这个defer进行解锁;
	s.data[key] = val

	//s.mutex.Lock()
	//s.data[key] = val
	// 发生panic;直接退出,不会释放锁;故多数建议使用defer s.mutex.Unlock()进行解锁;
	//s.mutex.Unlock()
}

func (s *SafeMap[K, V]) Get(key K) (any, bool) {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	res, ok := s.data[key]
	return res, ok
}

func (s *SafeMap[K, V]) LoadOrStore(key K, newVal V) (val V, loaded bool) {
	s.mutex.RLock()
	res, ok := s.data[key]
	s.mutex.RUnlock()
	if ok { // 存在则返回
		return res, true
	}
	
	// 不存在则新建
	s.mutex.Lock()
	defer s.mutex.Unlock()

	// double check 写法
	res, ok = s.data[key]
	if ok { // 存在则返回
		return res, true
	}
	s.data[key] = newVal
	return newVal, false
}

// safe_map_test.go
package sync

import (
	"testing"
	"time"
)

func TestSafeMap_LoadOrStore(t *testing.T) {
	s := &SafeMap[string, string]{
		data: make(map[string]string),
	}

	/**
	没有加double check,输出:
	=== RUN   TestSafeMap_LoadOrStore
	    safe_map_test.go:15: 协程1 value:  value1 false
	    safe_map_test.go:20: 协程2 value:  value2 false
	--- PASS: TestSafeMap_LoadOrStore (1.41s)

	加double check,输出:
	=== RUN   TestSafeMap_LoadOrStore
	    safe_map_test.go:27: 协程2 value:  value1 true
	    safe_map_test.go:22: 协程1 value:  value1 false
	--- PASS: TestSafeMap_LoadOrStore (1.01s)
	*/
	go func() {
		val, ok := s.LoadOrStore("key1", "value1")
		t.Log("协程1 value: ", val, ok)
	}()

	go func() {
		val, ok := s.LoadOrStore("key1", "value2")
		t.Log("协程2 value: ", val, ok)
	}()

	time.Sleep(time.Second)
}

double-check 写法

使用 RWMutex 实现 double-check:

  • 加读锁先检查一遍
  • 释放读锁
  • 加写锁
  • 再检查一遍
posted @ 2023-05-25 00:00  大西瓜Paul  阅读(18)  评论(0编辑  收藏  举报
/*增加返回顶部按钮*/ 返回顶部 /*给标题增加蓝色背景长条*/