并发concurrency

很多人都是冲着Go大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine只是官方实现的超级“线程池”而已。不过话说回来,每个实例4-5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造Go号称高并发的根本原因。另外,goroutine的简单易用,也在语言层面上给予了开发者巨大的便利。
Go并发concurrency - 图1
并发不是并行:
并发主要由切换时间片来实现“同时”运行,而并行则是直接利用多核实现多线程的运行,但Go可以设置使用核数,以发挥多核计算机的能力。
Goroutine奉行通过通信来共享内存,而不是共享内存来通信。

Channel

Channel是goroutine沟通的桥梁,大都是阻塞同步的
通过make创建,close关闭
Go并发concurrency - 图2

Channel是引用类型
可以使用for range来迭代不断操作channel
Go并发concurrency - 图3
可以设置单向或双向通道
可以设置缓存大小,在未被填满之前不会发生阻塞(无缓存的是同步的, 有缓存的是异步的)

Select

可处理一个或多个channel的发送与接收
同时有多个可用的channel时按随机处理
可用空的select来阻塞main函数
Go并发concurrency - 图4

可设置超时
Go并发concurrency - 图5

Go语言中并发程序依靠的是:goroutine和channel

goroutine
对于初选者,goroutine直接理解成线程就可以了。当对一个函数调用go,启动一个goroutine的时候,就相当于启动了一个线程,执行这个函数。

实际上,一个goroutine并不相当于一个线程,goroutine的出现正是为了替代原来的线程概念称为最小的调度单位。一旦运行goroutine时,先去当前线程查找,如果线程阻塞了,则被分配到空闲的线程,如果没有空闲的线程,那么就会新建一个线程。值得注意的是,当goroutine执行完毕后,线程不会回收退出,而是成了空闲的线程。
Go并发concurrency - 图6
这里的第18行为什么要sleep?这里是为了等上面两个go ready处理完成

Channel
channel的意思可以这么理解:主线程告诉大家你开goroutine可以,但是我在我的主线程开了一个管道,你做完了你要做的事情之后,往管道里面塞个东西告诉我你已经完成了。
Go并发concurrency - 图7
从上面的程序得到几点信息
1.channel只能使用make来进行创建
基本格式是 c := make(chan int)
int是说明这个管道能传输什么类型的数据

2.往channel中插入数据的操作
c <- 1

3.从channel中输入数据
<-c

4.为什么要输出两次(26和27行)
因为21和22行启动了两个goroutine,每个goroutine都往管道输出一个1,因此主线程要接收两次才能说明两个goroutine都结束了

channel分为两种,一种是有buffer的,一种是没有buffer的,默认的是没有buffer的
Go并发concurrency - 图8

有buffer的channel,要注意“放”先于“取”

  1. var a string
  2. var c = make(chan int, 10)
  3. func f() {
  4. a = "hello, world"
  5. c <- 0
  6. }
  7. func main() {
  8. go f()
  9. <-c
  10. print(a)
  11. }

无buffer的channel,要注意“取”先于“放”

  1. var a string
  2. var c = make(chan int)
  3. func f() {
  4. a = "hello, world"
  5. <-c
  6. }
  7. func main() {
  8. go f()
  9. c <- 0
  10. print(a)
  11. }

Go并发concurrency - 图9