专栏首页Golang语言社区Go中的同步与锁

Go中的同步与锁

最近学习了Go语言中同步包中的互斥锁、读写锁、Once、waitGroup。在并发程序开发的过程中,这两种锁是非常重要的,包括对共享资源进行访问控制的时候。sync是Go语言中的标准库。

Mutex 互斥锁

  • 互斥锁是传统并发程序对共享资源进行访问控制的主要手段。是sync包中的Mutex结构体。
    • type Mutex struct {}
  • 该结构体包括了两个方法,可以说是非常简单使用的
    • func (m *Mutex) Lock() {}
    • func (m *Mutex) Unlock() {}
  • 我们通过一个简单的例子来说明他的用法:
package main

import (

	"sync"

	"fmt"

)

type safeCounter struct {

	number int

	sync.Mutex

}

func (sc *safeCounter) Increment() {

	sc.Lock()

	sc.number++

	sc.Unlock()

}

func (sc *safeCounter) Decrement() {

	sc.Lock()

	sc.number--

	sc.Unlock()

}

func (sc *safeCounter) getNumber() int {

	sc.Lock()

	number := sc.number

	sc.Unlock()

	return number

}

func main() {

	sc := new(safeCounter)

	for i := 0; i < 100; i++ {

		go sc.Increment()

		go sc.Decrement()

	}

	fmt.Println(sc.getNumber())

}
  • 上述例子通过互斥锁来保证number整型变量在使用goroutine的情况下,持续稳定地输出0的结果。当然你可以试一下,去掉锁的保护,是否还可以稳定输出0?

RWMutex 读写锁

  • 读写锁是针对读写操作的互斥锁。遵循两个原则即可
    • 可以随便地读,并且支持多个goroutine同时读取
    • 写的时候,既不能读也不能写。
  • RWMutex 提供了四个方法
    • func (*RWMutex) Lock
    • func (*RWMutex) Unlock
    • func (*RWMutex) RLock
    • func (*RWMutex) RUnlock
  • 通过读写map的一个例子来说明RWMutex的使用方法:
package main

import (

	"fmt"

	"math/rand"

	"sync"

	"time"

)

type MapCounter struct {

	m map[int]int

	sync.RWMutex

}

func (mapCounter *MapCounter) Reader(n int) {

	for {

		mapCounter.RLock()

		v := mapCounter.m[rand.Intn(n)]

		mapCounter.RUnlock()

		fmt.Println(v)

		time.Sleep(1 * time.Second)

	}

}

func (mapCounter *MapCounter) Writer(n int) {

	for i := 0; i < n; i++ {

		mapCounter.Lock()

		mapCounter.m[i] = i * 10

		mapCounter.Unlock()

		time.Sleep(1 * time.Second)

	}

}

func main() {

	mc := MapCounter{m: make(map[int]int)}

	go mc.Writer(10)

	go mc.Reader(10)

	go mc.Reader(10)

	time.Sleep(15 * time.Second)

}

Once

  • Once 顾名思义,就是只执行一次 。通过一个简单的例子就可以说明:
package main

import (

	"fmt"

	"sync"

)

func main() {

	once := sync.Once{}

	done := make(chan bool)

	for i := 0; i < 10; i++ {

		go func() {

			once.Do(sayHello)

			done <- true

		}()

	}

	for i := 0; i < 10; i++ {

		fmt.Println(<-done)

	}

}

func sayHello() {

	fmt.Println("Hello")

}
  • 以上的例子只输出了一个“Hello” ,其余的都是true。

WaitGroup

  • WaitGroup 是可以一个goroutine集合,当集合里的goroutine执行完毕后,这个WaitGroup就会自动结束。你可以定义这个WaitGroup的大小。他里面只有三个方法:
    • func (wg *WaitGroup) Add(delta int) {} //定义大小
    • func (wg *WaitGroup) Done() {}
    • func (wg *WaitGroup) Wait() {}
  • 具体用法,通过下面的小例子说明。
package main

import (

	"fmt"

	"sync"

)

func main() {

	wg := sync.WaitGroup{}

	for i := 0; i <= 5; i++ {

		wg.Add(1)

		go func(i int) {

			defer wg.Done()

			fmt.Println("Work done for ", i)

		}(i)

	}

	wg.Wait()

}
  • 建议大家可以把上面的代码都跑一遍,方便自己理解代码的含义。结合官方文档来理解会更加容易。

参考

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-05-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • package runtime

    runtime包提供和go运行时环境的互操作,如控制go程的函数。它也包括用于reflect包的低层次类型信息;参见reflect报的文档获取运行时类型系统的可...

    李海彬
  • package debug

    Package debug contains facilities for programs to debug themselves while they ar...

    李海彬
  • go语言的匿名函数

    1-声明一个匿名函数 func(参数列表) 返回值列表 { 函数体… } 2-匿名函数的调用

    李海彬
  • 腾讯云认证考试常见问题答疑

    认证报名官网:https://cloud.tencent.com/edu/training,您需要先注册腾讯云帐号,并通过个人身份认证,完成个人认证后在该页面选...

    KLKL
  • yii2控制台执行

    Text-to-speech function is limited to 200 characters

    botkenni
  • 分布式作业 Elastic-Job-Lite 源码分析 —— 注册中心

    ZookeeperRegistryCenter,基于 Zookeeper 注册中心。从上面的类图可以看到,ZookeeperRegistryCenter 实现 ...

    芋道源码
  • 【侠客行】Lombok深度解析

    Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。例如开发中经常需要写的javabean,都需要花时间去添加相应的getter/sett...

    大蕉
  • 我扒了半天源码,终于找到了Oauth2自定义处理结果的最佳方案!

    https://github.com/macrozheng/springcloud-learning/tree/master/micro-oauth2

    macrozheng
  • Android开发笔记(四十九)异步任务处理AsyncTask

    Thread+Handler方式虽然能够实现多线程的通信处理,但是写起代码来颇为繁琐,所以Android提供了AsyncTask这样一个轻量级的异步任务类,...

    用户4464237
  • 运营商大数据:行走在机遇与危险边缘

    大数据文摘

扫码关注云+社区

领取腾讯云代金券