go语言多线程学习笔记——互斥锁

最近在学go语言的多线程,作为一门云时代的语言,并发处理应该是面试和应用中必不可少的一部分,强制自己写个笔记,好好整理一下,本人纯小白,只是课堂知识的自我消化,如果能帮助到大家,求之不得。

一、不加锁

多线程中使用睡眠函数不优雅,直接用sync.WaitGroup保证一个goroutine刚退出就可以继续执行,不需要自己猜需要sleep多久。

package main
import (
	"fmt"
	"sync"
)
var wg sync.WaitGroup
func main() {
	wg.Add(10) // 启动一个goroutine就登记+1,启动十个就+10
	var count=0
	for i:=0;i<10;i++{
		go func(){
			defer wg.Done() // goroutine结束就登记-1
			for j:=0;j<100000;j++{
				count++
			}
		}()
	}
	wg.Wait() // 等待所有登记的goroutine都结束
	fmt.Print(count)
}

启动十个goroutine对count自增10w次,理想状况为100w,但由于没有锁,会出现实际情况远远小于并且不相等的情况。
在这里插入图片描述
为什么会出现这样的结果?因为自增并不是一个原子操作,很可能几个goroutine同时读到同一个数,自增,又将同样的数写了回去,汇编代码如下:

MOVQ "".count(SB), AX
LEAQ 1(AX), CX
MOVQ CX, "".count(SB)

二、互斥锁

(1)直接使用锁
共享资源是count变量,临界区是count++,临界区之前加锁,使其他goroutine在临界区阻塞,离开临界区解锁,就可以解决这个data race的问题。go语言是通过sync.Mutex实现这一功能。

package main

import (
	"fmt"
	"sync"
)
var wg sync.WaitGroup
func main() {
	var mu sync.Mutex    //多
	wg.Add(10)
	var count = 0
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done() 
			for j := 0; j < 100000; j++ {
				mu.Lock()     //多
				count++
				mu.Unlock()    //多
			}
		}()
	}
	wg.Wait()
	fmt.Print(count)
}

为方便观察,多出的三行已经加上注释,使用很简单,执行,也干脆利落的输出了100w。
在这里插入图片描述
(2)嵌入字段方式使用锁
把Mutex嵌入struct,可以直接在这个struct上使用Lock/Unlock。

package main
import (
	"fmt"
	"sync"
)
var wg sync.WaitGroup
type Counter struct{
	sync.Mutex
	Count uint64
}
func main() {
	var counter Counter
	wg.Add(10) 
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done() 
			for j := 0; j < 100000; j++ {
				counter.Lock()
				counter.Count++
				counter.Unlock()
			}
		}()
	}
	wg.Wait() 
	fmt.Print(counter.Count)
}

结果不必多说,也是干脆的100w。(祝大家的年薪都超过这个数)
(3)把加/解锁封装成方法

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

type Counter struct{
	mu sync.Mutex
	count uint64
}
func (c *Counter)Incr(){
	c.mu.Lock()
	c.count++
	c.mu.Unlock()
}
func (c*Counter)Count()uint64{
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.count
}
func main() {
	var counter Counter
	wg.Add(10) // 启动一个goroutine就登记+1,启动十个就+10
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done() // goroutine结束就登记-1
			for j := 0; j < 100000; j++ {
				counter.Incr()
			}
		}()
	}
	wg.Wait() // 等待所有登记的goroutine都结束
	fmt.Print(counter.Count())
}

加锁操作和解锁操作被封装成了方法,顺便复习一下方法的使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值