# golang 数据结构 2

golang 数据结构1

  • channel
  • atomic
  • sync.Mutex
  • sync.WaitGroup
  • sync.Pool
  • sync.Map

channel

基本用法

var ch chan int 
ch := make(chan int)
ch := make(chan int, 2) // 带缓存 chan

ch <- value // 给 channel 发送值
value := <- ch // 从 channel 接收值

// for range channel
for value := range ch {
}

// 函数中使用
func run(ch chan int){}
func run(ch <-chan int) {} // 指定是一个只能接收 channel 
func run(ch chan<- int) {} // 指定是一个只能发送 channel 

close(ch) // 关闭 channel,关闭后不能再往 channel 发送消息

atomic

https://golang.org/pkg/sync/a…

各种原子操作

var data int32
data = atomic.AddInt32(data, 1) // int类型原子操作,还有方法:AddInt64,AddUint32 etc.

// 支持任何类型
var value atomic.Value

value.Store(data) // 存储
data := value.Load().(int32) // 获取

// 进行 compare-and-swap(CAS) 操作
swaped := atomic.CompareAndSwapInt32(&data, data, 2)

atomic.Value 可以使用在项目中存储 config。

比较并交换 CAS

sync.Mutex

Go 教程系列笔记 Mutex(互斥锁)

// 互斥锁一般配合 struct 一起用
clazz := struct {
    data int
    sync.Mutex
}

clazz.Lock() // 申请锁
clazz.data++
clazz.Unlock() // 释放锁

sync.RWMutex

读写锁是可以获得任意多的读锁和一个写锁。

// 互斥锁一般配合 struct 一起用
clazz := struct {
    data int
    sync.Mutex
}

clazz.RLock() // 申请读锁
fmt.Println(clazz.data)
clazz.RUnlock() // 释放读锁

clazz.Lock() // 申请写锁
clazz.data++
clazz.Unlock() // 释放写锁

在获取写锁时,必须读锁和写锁都必须释放后才能获得。不然会进入阻塞。

sync.Pool

Pool 是一组可以单独保存和获取的临时对象。

任何存储在 Pool 的元素可能在任何时候被删除而不会通知。

Pool 在多个 goroutines 同时使用是线程安全的。

Pool 目的是缓存已分配但未使用的元素。以减少内存分配,缓解 GC 的压力。

基本使用

var bufPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func Log(w io.Writer, key, val string) {
    b := bufPool.Get().(*bytes.Buffer)
    b.Reset() // Pool 获取的元素都需要做初始化
    
    b.WriteString(time.Now().Format(time.RFC3339))
    b.WriteByte(' ')
    b.WriteString(key)
    b.WriteByte('=')
    b.WriteString(val)
    w.Write(b.Bytes())
    bufPool.Put(b) // 放回 Pool
}

// Pool 定义
type Pool struct {
    // New 设置一个函数用来创建元素
    New func() interface{}
}

// Pool 方法
func (p *Pool) Get() interface{} // 获取元素
func (p *Pool) Put(x interface{}) // 放回元素

sync.Pool 的问题是,Pool 中的对象会随时被 GC 清理掉,这使得 sync.Pool 只适合做简单的对象池,而不适合做连接池(http.Client,redis.Client 这类连接对象)。

https://studygolang.com/artic…

sync.Map

https://golang.org/pkg/sync/#Map

使用 sync.Map 的原因是 golang 的 map 的读写不是并发安全的。

Map 是像 map[interface{}]interface{} 但在多个 goroutines 使用是并发安全。

Map 类型优化的两个点:

  • (1) 当一个键值对只写一次,但读很多次,就像在只增长的缓存中一样。
  • (2) 当多个 goroutines 读取、写入和覆盖不相交的键值对时。

当是这两类情况下,比起 map 搭配 Mutex 或 RWMutex,使用 Map 可以显著减少锁的争用情况。

基本使用

var hash sync.Map

hash.Store("name", "leon") // 存储 key, value
value, ok := hash.Load("name") // 读取

actual, loaded := hash.LoadOrStore("name", "leonXu") // 读取或更新

hash.Delete("name") // 删除

final = make(map[interface{}]interface{})
// 循环 Map
hash.Range(func(k, v interface{}) bool {
    final[k] = v
    return true
})

Go 1.9 sync.Map揭秘

Ref

    原文作者:xfstart07
    原文地址: https://segmentfault.com/a/1190000020048220
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞