Parts of A Tour Of Go
Go
程 Goroutine
Go程
是由Go
运行时管理的轻量级线程。
1 | // 会启动一个新的Go程并执行 f(x, y, z) |
Go
程在相同的地址空间中运行,因此在访问共享内存时必须进行同步。sync
包提供了这种能力。
1 | package main |
信道channel
信道是带有类型的管道
信道使用信道操作符<-
来发送值或接收值<-
箭头指向就是数据流的方向
1 | // 信道在使用前必须创建 |
默认情况下,发送和接收操作在另一端准备好之前都会阻塞(同步调用)
1 | package main |
信道缓冲
信道是可以带缓冲的
将缓冲长度作为第二个参数提供给make
来初始化一个带缓冲的信道
1 | ch := make(chan int, 100) |
仅当信道的缓冲区填满后,向其发送数据时才会阻塞(等待数据取出)
当缓冲区为空时,接收方会阻塞(等待数据到达)
1 | package main |
range
和close
发送者可通过close
关闭一个信道来表示没有需要发送的值了
1 | // 接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭 |
循环for i := range ch
会不断从信道接收值,直到ch
被关闭
注意:
- 建议由发送者关闭信道,而不是接收者
- 向一个已经关闭的信道发送数据会报错
panic
- 信道与文件不同,通常情况下也无需关闭它们
- 只有在必须告诉接收者不再有值需要发送的时候才有必要关闭,例如终止
range
循环
1 | package main |
select
语句
select
语句使一个Go
程可以等待多个通信操作select
会阻塞到某个分支可以继续执行为止,并执行该分支
当多个分支都准备好时会随机选择一个执行
1 | package main |
select
默认选择
当select
中的其它分支都没有准备好时,default
分支就会执行
为了在尝试发送或接受时不发生阻塞,可使用default
分支
1 | select { |
1 | package main |
练习: 等价二叉树
1 | // tree 包 |
函数tree.New(k)
用于构造一个随机结构的二叉树,保存了值(k,2k,3k…10k)
1 | package main |
sync.Mutex
互斥锁
信道非常适合在各个Go
程间进行通信
要实现每次只有一个Go
程能访问一个共享的变量,为了避免冲突,可以使用互斥锁Go
标准库提供了sync.Mutex
互斥锁类型 和 Lock
Unlock
两个方法
可以在代码前调用Lock
方法,在代码后调用Unlock
方法来保证一段代码的互斥执行
也可以用defer
语句来保证互斥锁一定会被解锁
1 | package main |
练习: Web爬虫
使用Go
的并发特性来并行化一个Web爬虫
修改Crawl
函数来并行抓取URL,并保证不重复
可以用一个map
来缓存已经获取的URL,但是map
本身并不是并发安全的
1 | package main |