1)请注意,返回一个局部变量的地址完全没有问题,这点与C不同。该局部变量对应的数据 在函数返回后依然有效。
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := File{fd, name, nil, 0} return &f }
2)make(T,
args)
的目的不同于new(T)
。它只用于创建切片、映射和信道,并返回类型为T
(而非*T
)的一个已初始化 (而非置零)的值。
make([]int, 10, 100)
会分配一个具有100个int
的数组空间,接着创建一个长度为10, 容量为100并指向该数组中前10个元素的切片结构。
切片是对数组的封装。
3)Map
与切片一样,映射也是引用类型。 若将映射传入函数中,并更改了该映射的内容,则此修改对调用者同样可见。
4)%T
,它会打印某个值的类型
5)常量
type ByteSize float64 const ( // 通过赋予空白标识符来忽略第一个值 _ = iota // ignore first value by assigning to blank identifier KB ByteSize = 1 << (10 * iota) MB GB TB PB EB ZB YB )
6)channel
信道有很多惯用法,我们从这里开始了解。在上一节中,我们在后台启动了排序操作。 信道使得启动的Go程等待排序完成。
c := make(chan int) // 分配一个信道 // 在Go程中启动排序。当它完成后,在信道上发送信号。 go func() { list.Sort() c <- 1 // 发送信号,什么值无所谓。 }() doSomethingForAWhile() <-c // 等待排序结束,丢弃发来的值。
接收者在收到数据前会一直阻塞。若信道是不带缓冲的,那么在接收者收到值前, 发送者会一直阻塞;若信道是带缓冲的,则发送者仅在值被复制到缓冲区前阻塞; 若缓冲区已满,发送者会一直等待直到某个接收者取出一个值为止。
带缓冲的信道可被用作信号量,例如限制吞吐量。在此例中,进入的请求会被传递给 handle
,它从信道中接收值,处理请求后将值发回该信道中,以便让该 “信号量”准备迎接下一次请求。信道缓冲区的容量决定了同时调用 process
的数量上限,因此我们在初始化时首先要填充至它的容量上限。
var sem = make(chan int, MaxOutstanding) func handle(r *Request) { sem <- 1 // 等待活动队列清空。 process(r) // 可能需要很长时间。 <-sem // 完成;使下一个请求可以运行。 } func Serve(queue chan *Request) { for { req := <-queue go handle(req) // 无需等待 handle 结束。 } }
然而,它却有个设计问题:尽管只有 MaxOutstanding
个Go程能同时运行,但 Serve
还是为每个进入的请求都创建了新的Go程。其结果就是,若请求来得很快, 该程序就会无限地消耗资源。为了弥补这种不足,我们可以通过修改 Serve
来限制创建Go程,这是个明显的解决方案,但要当心我们修复后出现的Bug。
func Serve(queue chan *Request) { for req := range queue { sem <- 1 go func() { process(req) // 这儿有Bug,解释见下。 <-sem }() } }
Bug出现在Go的 for
循环中,该循环变量在每次迭代时会被重用,因此 req
变量会在所有的Go程间共享,这不是我们想要的。我们需要确保 req
对于每个Go程来说都是唯一的。有一种方法能够做到,就是将 req
的值作为实参传入到该Go程的闭包中:
func Serve(queue chan *Request) { for req := range queue { sem <- 1 go func(req *Request) { process(req) <-sem }(req) } }
另一种管理资源的好方法就是启动固定数量的 handle
Go程,一起从请求信道中读取数据。Go程的数量限制了同时调用 process
的数量。Serve
同样会接收一个通知退出的信道, 在启动所有Go程后,它将阻塞并暂停从信道中接收消息。
func handle(queue chan *Request) { for r := range queue { process(r) } } func Serve(clientRequests chan *Request, quit chan bool) { // 启动处理程序 for i := 0; i < MaxOutstanding; i++ { go handle(clientRequests) } <-quit // 等待通知退出。 }
有疑问加站长微信联系(非本文作者)