写在前面
并发是一个比较宽泛的概念,是指计算机能够同时执行多个任务,对于同时进行的任务数大于cpu数的情况,并发就牵扯到任务的调度问题。Java、C++等语言都支持并发编程,Go当然也不例外,同时由于go有协程,go的并发编程是有其独特之处的。
使用goroutine
goroutine是go的特性之一,可以使用关键字go快速启动并发的goroutine,语法格式为go 函数名(函数参数) 比如我们并发打印10个数字,示例函数如下
func printNum(num int) {
fmt.Println(num)
}
func main() {
for i := 0; i < 10; i++ {
go printNum(i)
}
fmt.Println("main")
time.Sleep(time.Second)
}
可以发现打印的顺序并不是0-10,而是错序打印的,同时可以观察到“main”的顺应也并不是固定的,即main函数也在这个调度的过程中,这展示了并发的调度,同时我们可以发现,如果没有time.Sleep语句强制停留一段时间,基本上程序会立即结束,看不到printNum函数的调用。
channel的使用
- channel可以很容易地实现生产消费模型,同时由于channel可以分为有缓存和无缓存,有缓存和无缓存之间的主要区别在于无缓存channel读写两端只要有一方不工作,另一方就要阻塞,而有缓存channel只要有数据,两端就可以各自工作。
- 下述程序是一个有缓冲的channel,用来快速输出0-50所有数的十倍
- 另外非常重要的是不要忘记在结束时关闭通道资源,如果两端都不关闭且不进行操作,那么程序将会陷入死锁状态。
- 值得一提的是,go在一定程度上是可以检测出死锁状态的,比如下述程序如果忘记close channel,那么会提示all goroutines are asleep - deadlock!,可以帮助我们发现死锁错误。
c := make(chan int, 10)
d := make(chan int, 10)
go func() {
for i := 0; i < 50; i++ {
c <- i
}
close(c)
}()
go func() {
for i := range c {
d <- i * 10
}
close(d)
}()
for i := range d {
fmt.Println(i)
}
select的使用
- select语句只能用于通道的操作,select会监听指定通道的操作,当有通道准备好便会指向对应的代码,如果有多个通道准备好,则随机选取一个。
- 该程序可以随机打印c或者d或者no recv,对于select,当上述的条件都没有被执行的时候,select会执行default对应的语句。
- 值得一提的是,如果select中有default,则执行default语句,如果没有,那么select会进行阻塞,直到有条件被触发进而执行对应的语句。
c := make(chan string)
d := make(chan string)
go func() {
for {
c <- "c"
}
}()
go func() {
for {
d <- "d"
}
}()
for {
select {
case msg1 := <-c:
fmt.Println("received", msg1)
case msg2 := <-d:
fmt.Println("received", msg2)
default:
fmt.Println("no recv")
锁的使用
锁是保证并发安全的过程中十分重要的一种手段,Go提供了标准库的sync锁,其保证了并发的安全性,包括了互斥锁、读写锁、并发安全字典、条件同步变量等内容
-
Mutex互斥锁
- 互斥锁有Lock和Unlock两个方法,互斥锁的作用主要是保证某一段操作能够原子地执行,即一段操作在执行中途不会被其他工作过程打断。
- 一个十分简单的用到锁的场景
lock.Lock() x = x + 1 lock.Unlock()
-
RWMutex读写锁
- 读写锁保证只有一个一个协程在写的状态,但是可以保证多个协程在读的状态
- 读写锁的设计保证了读的并发效率同时也保证了数据的一致性
-
WaitGroup
- WaitGroup有Add、Done、Wait方法
- 作用主要是等待一组goroutine执行完成,用来阻塞主程序,避免主程序执行完退出但协程还没有执行的情况
- 可以用Add方法增加计数器,用Done方法减少计数器,Wait方法将阻塞到计数器为0
总结
并发是一种非常重要的技术,是非常复杂的,但也是与我们的实际生产分不开的,但万变不离其宗,我们了解并发的基本机制、锁的使用是我们入门并发的第一步,可以通过实际生产和相关文章的学习来精进自己的并发功力。