Go语言---sync 包

sync包提供基本的同步原语,如互斥锁。 除了Once和WaitGroup类型之外,大多数类型都是供低级库例程使用的。经过通道和通信可以更好地完成更高级别的同步。

互斥锁

互斥锁用来保证在任一时刻,只能有一个线程函数访问某对象,保证共享数据的安全性防止竞态。Mutex 的初始值为解锁状态。Mutex 通常作为其它结构体的匿名字段使用,使该结构体具有 Lock 和 Unlock 方法。  Mutex 可以安全的在多个例程中并行使用。

// Locker 接口包装了基本的 Lock 和 UnLock 方法,用于加锁和解锁。
type Locker interface {
    Lock()
    Unlock()
}
type Mutex struct {
        // contains filtered or unexported fields  //字段没有导出
}
// Lock 用于锁住 m,如果 m 已经被加锁,则 Lock 将被阻塞,直到 m 被解锁。
func (m *Mutex) Lock()

// Unlock 用于解锁 m,如果 m 未加锁,则该操作会引发 panic。
func (m *Mutex) Unlock()

程序示例:

package main

import (
	"sync"
	"fmt"
)

// 示例:互斥锁
type SafeInt struct {
	sync.Mutex
	Num int
}

func main() {
	count := SafeInt{}

	done := make(chan bool)
	for i := 0; i < 10; i++ {
		go func(i int) {
			count.Lock() // 加锁,防止其它例程修改 count
			//保护共享变量 count,+=操作不是原子操作   顺序不定,不知道哪个协程先执行,但每次只有一个协程访问变量
			count.Num += i
			fmt.Print(count.Num, " ")
			count.Unlock() // 修改完毕,解锁
			done <- true
		}(i)
	}
	for i := 0; i < 10; i++ {
		<-done
	}

	fmt.Println(count.Num)   // 45
}

读写互斥锁

RWMutex 比 Mutex 多了一个“读锁定”和“读解锁”,可以让多个例程同时读取某对象。RWMutex 的初始值为解锁状态。RWMutex 通常作为其它结构体的匿名字段使用。Mutex 可以安全的在多个例程中并行使用。

一次只有一个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁,可以提高并发性。

(1)、当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞。

(2)、当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进行加锁, 它必须阻塞知道所有的线程释放锁。

(3)、通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁长期占用, 而等待的写模式锁请求长期阻塞。

实用场景:读写锁适合于对数据结构的读次数比写次数多得多的情况。 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁又叫共享-独占锁.
// Lock 将 rw 设置为写锁定状态,禁止其他例程读取或写入。
func (rw *RWMutex) Lock()

// Unlock 解除 rw 的写锁定状态,如果 rw 未被写锁定,则该操作会引发 panic。
func (rw *RWMutex) Unlock()

// RLock 将 rw 设置为读锁定状态,禁止其他例程写入,但可以读取。
func (rw *RWMutex) RLock()

// Runlock 解除 rw 的读锁定状态,如果 rw 未被读锁顶,则该操作会引发 panic。
func (rw *RWMutex) RUnlock()

// RLocker 返回一个互斥锁,将 rw.RLock 和 rw.RUnlock 封装成了一个 Locker 接口。
func (rw *RWMutex) RLocker() Locker

组等待

  WaitGroup 用于等待一组例程的结束。主例程在创建每个子例程的时候先调用 Add 增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后,主例程通过 Wait 方法开始等待,直到计数器归零才继续执行。

// 计数器增加 delta,delta 可以是负数。
func (wg *WaitGroup) Add(delta int)

// 计数器减少 1
func (wg *WaitGroup) Done()

// 等待直到计数器归零。如果计数器小于 0,则该操作会引发 panic。
func (wg *WaitGroup) Wait()

条件变量

  条件等待当条件不满足时通过 Wait 函数让一个例程等待,另一个线程改变条件并通过Signal 发送信号让一个等待的例程继续,通过 Broadcast 让所有等待的例程继续。

  在 Wait 之前应当手动为 c.L 上锁,Wait 结束后手动解锁。为避免虚假唤醒,需要将 Wait 放到一个条件判断循环中。官方要求的写法如下:
c.L.Lock()
for !condition() {
    c.Wait()
}
// 执行条件满足之后的动作...
c.L.Unlock()

Cond 在开始使用之后,不能再被复制。
type Cond struct {
    L Locker // 在“检查条件”或“更改条件”时 L 应该锁定。
} 

// 创建一个条件等待    初始化互斥锁变量,返回Cond变量
func NewCond(l Locker) *Cond

// Broadcast 唤醒所有等待的 Wait,建议在“更改条件”时锁定 c.L,更改完毕再解锁。
func (c *Cond) Broadcast()

// Signal 唤醒一个等待的 Wait,建议在“更改条件”时锁定 c.L,更改完毕再解锁。
func (c *Cond) Signal()

// Wait 会解锁 c.L 并进入等待状态,在被唤醒时,会重新锁定 c.L
func (c *Cond) Wait()

程序示例:

// 示例:条件等待
func main() {
	condition := false // 条件不满足
	var mu sync.Mutex  //                       锁 1、保护条件变量   2、 保护等待队列
	cond := sync.NewCond(&mu)
	// 让例程去创造条件
	go func() {
		mu.Lock()
		condition = true // 更改条件
		cond.Signal()    // 发送通知:条件已经满足
		mu.Unlock()
	}()
	mu.Lock()
	// 检查条件是否满足,避免虚假通知,同时避免 Signal 提前于 Wait 执行。
	for !condition {
		// 等待条件满足的通知,如果收到虚假通知,则循环继续等待。   =======这里wait函数的实现 会有解锁操作,等待唤醒等待队列中的线程
		cond.Wait() // 等待时 mu 处于解锁状态,唤醒时重新锁定。   //wait函数实现  类似于  linux条件变量
	}
	fmt.Println("条件满足,开始后续动作...")
	mu.Unlock()
}

// 输出结果:
// 条件满足,开始后续动作...

单次执行

  Once 的作用是多次调用但只执行一次,Once 只有一个方法,Once.Do(),向 Do 传入一个函数,这个函数在第一次执行 Once.Do() 的时候会被调用,以后再执行 Once.Do() 将没有任何动作,即使传入了其它的函数,也不会被执行,如果要执行其它函数,需要重新创建一个 Once 对象。Once 可以安全的在多个例程中并行使用。

// 多次调用仅执行一次指定的函数 f
func (o *Once) Do(f func())

程序示例:

func main() {
	var once sync.Once
	onceBody := func() {
		fmt.Println("Only once")
	}
	done := make(chan bool)
	for i := 0; i < 10; i++ {
		go func() {
			once.Do(onceBody) // 多次调用只执行一次
			done <- true
		}()
	}
	for i := 0; i < 10; i++ {
		<-done
	}
}

Map类型

Map类型跟Go语言中 map [interface {}] interface {}类似,但是对于多个goroutine并发使用而不需要额外的锁定或同步是安全的。Map类型是定制的,主要是保证单独使用时保证并发执行的类型安全性。

Map类型针对两种常见使用情况进行了优化:
(1)给定键的入口只写入一次,但多次读取,仅仅在缓存中存在;

(2)在多个goroutine读取,写入和 覆盖不相交键集的条目。 

在这两种情况下,与使用单独互斥或RWMutex锁配对的Go类型相比,使用Map类型可以显着减少锁竞争。

//Map类型
type Map struct {
        // contains filtered or unexported fields
}

//删除key的value值
func (m *Map) Delete(key interface{})

//读取
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
//通过匿名函数f循环处理Map的键值对,返回false 就停止
func (m *Map) Range(f func(key, value interface{}) bool)
//写入
func (m *Map) Store(key, value interface{})
参考博客:http://www.cnblogs.com/golove/p/5918082.html

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值