并发安全锁与原子操作

[TOC]

#sync包-并发操作

sync.WaitGroup线程等待

使用sync.WaitGroup方法来等待其他goroutine完成

var wg sync.WaitGroup //引入sync.WaitGroup的方法

func goroute(i int) {
	defer wg.Done() //计数减一
	fmt.Println("hello", i)
	time.Sleep(time.Second)
}

func main() {
	wg.Add(2) //指定计数为2
	go func() {
		defer wg.Done()
		goroute(1)
		fmt.Println("匿名函数")
	}()
	wg.Wait() //等待计数为0,表示goroutine的线程都执行完成
	fmt.Println("main hello")
}

sync.Mutex互斥锁

是一个结构体.是值类型.给函数传参数的时候要传指针.

var (
	x    = 0
	wg   sync.WaitGroup
	lock sync.Mutex //定义锁
)

func add() {
	for i := 0; i < 100000; i++ {
		lock.Lock() //开启锁
		x = x + 1
		lock.Unlock() //关闭锁
	}
	wg.Done()
}

func main() {
	wg.Add(2)
	go add()
	go add()
	wg.Wait()
	fmt.Println(x)
}

sync.RWMutex读写锁

var (
	x      = 0
	wg     sync.WaitGroup
	lock   sync.Mutex
	rwLock sync.RWMutex //定义读写锁
)
func read() {
	defer wg.Done()
	rwLock.RLock() //开启读锁
	fmt.Println(x)
	time.Sleep(time.Millisecond * 2)
	rwLock.RUnlock() //关闭读锁
}
func writer() {
	defer wg.Done()
	rwLock.Lock() //开启写锁,并行变成串行
	x = x + 1
	time.Sleep(time.Millisecond * 10)
	rwLock.Unlock() //关闭写锁
}
func rwlockMain() {
	start := time.Now()
	for i := 0; i < 10; i++ {
		go writer()
		wg.Add(1)
	}
	for i := 0; i < 1000; i++ {
		go read()
		wg.Add(1)
	}
	wg.Wait()
	fmt.Println(time.Now().Sub(start))
}

sync.Once只执行一次

sync.Once其实内部包含一个互斥锁和一个布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成。这样设计就能保证初始化操作的时候是并发安全的并且初始化操作也不会被执行多次。

package main

import (
	"fmt"
	"sync"
)

var (
	wg   sync.WaitGroup
	once sync.Once
)

func f1(ch1 chan<- int) {
	defer wg.Done()
	for i := 0; i < 100; i++ {
		ch1 <- i
	}
	close(ch1)
}
func f2(ch1 <-chan int, ch2 chan<- int) {
	defer wg.Done()
	for {
		x, ok := <-ch1
		if !ok {
			break
		}
		ch2 <- x * x

	}
	f := func() {
		close(ch2)
	}
	once.Do(f) // 确保某个操作只执行一次
}
func main() {
	a := make(chan int, 100)
	b := make(chan int, 100)
	wg.Add(3)
	go f1(a)
	go f2(a, b)
	go f2(a, b)
	wg.Wait()
	for ret := range b {
		fmt.Println(ret)
	}
}

sync.Map并发安全map

普通map不支持并发

// go 内置的map不支持并发安全的,并发写入会出错
// 当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误。
var (
	m    = make(map[string]int)
	lock sync.Mutex
	wg   sync.WaitGroup
)

func get(key string) int {
	return m[key]
}
func set(key string, value int) {
	m[key] = value
}
func main() {
	for i := 0; i < 20; i++ {
		wg.Add(1)
		go func(n int) {
			key := strconv.Itoa(n)
			lock.Lock()
			set(key, n) //fatal error: concurrent map read and map write
			lock.Unlock()
			fmt.Printf("k=:%v,v:=%v\n", key, get(key))
			wg.Done()
		}(i)
	}
	wg.Wait()
}

sync.Map支持并发

sync.Map内置了StoreLoadLoadOrStoreDeleteRange等操作方法。

//sync.Map  是一个支持并发安全的map
var m2 = sync.Map{}
func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 21; i++ {
		wg.Add(1)
		go func(n int) {
			key := strconv.Itoa(n)
			m2.Store(key, n)         // 必须使用sync.Map内置的Store方法设置键值对
			value, _ := m2.Load(key) // 必须使用sync.Map提供的Load方法根据key取值
			fmt.Printf("k=:%v,v:=%v\n", key, value)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

sync/atomic包-原子操作

​ 在代码中加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好

// 原子操作
var x int64
var wg sync.WaitGroup
var lock sync.Mutex

func add() {
	// x++
	// lock.Lock()
	// x = x + 1
	// lock.Unlock()

	atomic.AddInt64(&x, 1)
	wg.Done()
}

func main() {
	wg.Add(100000)
	for i := 0; i < 100000; i++ {
		go add()
	}
	wg.Wait()
	fmt.Println(x)

	// 比较并交换
	// x = 200
	// ok := atomic.CompareAndSwapInt64(&x, 200, 300) //判断x是不是等于200,如果不等于就替换为300,并返回true
	// fmt.Println(ok, x)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值