这是我参与「第五届青训营」伴学笔记创作活动的第 5 天
前言
在并发情况下,存在资源竞争问题,程序开发过程中我们需要确保并发安全,本文将主要介绍go语言在管理共享变量上的作出的努力。
详细知识点介绍
竞争条件
当并发线程交错执行代码时,针对同一变量的访问容易产生数据最终结果与预期不一致的问题,也就是数据竞争。
数据竞争产生的条件:
- 两个以上的goroutine并发访问相同的变量。
- 至少有一个为写操作。
互斥
互斥是避免数据竞争的方法之一,在互斥条件下,程序允许多个goroutine去访问变量,但在同一时刻最多只能有一个goroutine在访问。
go语言包中的 sync 包提供了两种锁类型:sync.Mutex 和 sync.RWMutex。
sync.Mutex
使用buffered channel可以作为计数信号量,来控制最多同时执行的goroutine数,当channel容量为1时,就可以保证最多只有一个goroutine在同一时刻访问一个共享变量。当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能等到这个 goroutine 释放该 Mutex再获取。
sync.RWMutex
“多读单写”锁,允许多个只读操作并行执行,但写操作完全互斥。
其他方法
避免写变量
存在写操作时出现数据竞争的必要条件,如果所有的并发线程都只进行读操作而不写入,自然不会发生数据竞争。
示例:在读取map前先完成map中所有值的初始化,而不是读到对应的key再填值,避免”懒“填充。
通过通道共享数据
将变量限定在单独的goroutine中,并通过channel来进行数据通信,避免变量被多个线程同时操作。
原子函数
原子函数能够以很底层的加锁机制来同步访问整型变量和指针,方法会强制同一时刻只能有一个goroutine运行并完成对应操作,对应的包为"sync/atomic"。
课后个人总结
在多个线程同时访问共享变量并存在写操作时,容易出现数据竞争问题,为了避免数据竞争问题的出现,go语言的sync包提供了互斥锁和原子操作等工具实现数据同步。此外,我们也可以通过避免写操作、使用channel通信避免多线程访问数据等方式来破坏数据竞争问题产生的必要条件,从而保证并发安全。