1 Goroutine
是什么?如何使用?不同的goroutine如何访问相同的内存地址
是什么?
- 在Golang中是一个轻量级线程。是Go中的基本执行单位,从语言上实现并发。
如何使用?
- 在函数前面加上go:
f()
go f() //线程
- 访问共享内存需要通过信道chan
2 信道 chan
-
是什么?如何初始化?关于阻塞?
-
信道用于提供给不同的线程之间共享资源。是一个带有数据类型的管道
-
初始化:
ch := make(chan int)
orvar ch = make(chan int)
-
使用:
向管道写入数据:
ch <- 99
箭头右边是写入方向,箭头左边是接收方向 -
在进行写入or发送之前,另一端如果没有准备好,这一端都会被阻塞
var ch := make(chan int) ch <- 99 fmt.Print(<-ch) ---------output------- fatal error: all goroutines are asleep - deadlock!
原因:
程序是顺序执行的,当进行写入操作ch <- 99 时,没有配对的接收端(后面的读取操作还没执行到)。这时候需要加一个配对的接收端.
go func(){ ch <- 99 }() fmt.Print(<- ch)
上面代码中,使用了一个匿名函数,并加上goroutine,使main goroutine与go func() 同时执行,在func()中进行写入的时候,就有了接收对象。
-
-
带有缓冲的信道
- 信道内可以缓冲数据
- 实现:
make(chan int, 99)
后面加上缓冲格子的数量 - 什么时候阻塞?
- 缓冲区满了,发送方会阻塞
- 缓冲区空了,接收方会阻塞
- !attention!:阻塞会导致程序中断出错退出。如何防止出现阻塞?使用一个flag来记录此时管道中的元素个数。写入时++,读出时-- 。具体例子见:web爬虫
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) fmt.Println(<-ch) }
-
不断从信道接受值:range close? close如何使用?
- 接收值:
for i := range ch
实现不断从信道中获取值,直到信道关闭 - 关闭信道: 通常情况下无需关闭信道
- 只有写入端可以关闭信道.向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
- close(ch ) 来关闭信道
- 如何得知信道是否关闭:
val, ok := range ch
当信道关闭时,ok == false
func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x //相当于一个暂时存储的内容 x, y = y, x+y } close(c) } func main() { c := make(chan int, 20) go fibonacci(20, c) //这句话中go没有用 for i := range c { fmt.Println(i) } }
- 接收值:
3 select*
是什么?怎么用?
-
是什么?
- 用于一个goroutine中来同时等待多个管道。
- 当某个管道停止阻塞就执行,当多个管道停止阻塞,则随机选择一个管道执行。
- 当所有分支都无法执行时,可以执行default (不一定是管道相关语句)
-
怎么使用?
attention:
每一个case后面的语句也是在执行,比如case <-quit: , 这里已经从管道quit中读取了一个元素。那么如果quit没有缓冲区,在后面再执行quit读取内容输出会导致阻塞。
select { case c <- x: x++ case <-quit: fmt.Print("ss") default: pass }
-
实现例子:
仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
分析:
-
下面信道的容量是5,当信道满了的时候,写入就停止了,这个时候接收方开始运行。
当信道空了的时候,写入方继续运行。
-
在fib函数前需要加上go , 只有这样,main()协程和goroutine才能并行。否则,读取信道的功能,只有等到fib执行完毕才能运行。这时候信道满了,没有人读取,那么写入方将一直被阻塞
package main import ( "fmt" ) func fibonacci(n int, c
-