10 things you (probably) don’t know about golang
匿名结构体 (Anonymous structs)
Template data (模板数据)
data := struct {
Title string
Users []*User //猜测 User 是一个接口,接口指针的切片
} {
title,USERS,}
err := tmpl.Execute(w,data)
(Cheaper and safer than using map[string]interface{})
确实没有太理解,是什么意思?
嵌入式锁 (Embedded Lock)
var hits struct{
sync.Mutex //匿名对象的方法,(类似于一种继承的方式)
n int
}
hits.Lock() // (和C++中的继承很类似)
hits.n++
hits.Unlock()
Nested structs 嵌套的结构
Decoding deeply nested JSON data
{"data" : {"children" : [ {"data" : { "title" : "The Go homepage","url" : "http://golang.org/"}},... ]}}
type Item struct{
Titel string
URL string
}
type Response struct{ //使用 Structs 来表示 Json 数据,十分巧妙
Data struct {
Children []struct {
Data Item
}
}
}
记得在 golang的 json 库中,既可以直接使用这样的结构来进行 JSON 数据的解析和发送。
@H_404_84@Command-line godoc 命令行 godoc
% godoc sync Mutex //这里 godoc 是命令,sync是包名,Mutex是类型名
显示值如下
type Mutex struct {
// contains filtered or unexported fields }
A Mutex is a mutual exclusion lock. Mutexes can be created as part of
other structures; the zero value for a Mutex is an unlocked mutex.func (m *Mutex) Lock()
Lock locks m. If the lock is already in use,the calling goroutine
blocks until the mutex is available.func (m *Mutex) Unlock()
Unlock unlocks m. It is a run-time error if m is not locked on entry to
Unlock.A locked Mutex is not associated with a particular goroutine. It is allowed for one goroutine to lock a Mutex and then arrange for another goroutine to unlock it.
godc -src 可以直接显示golang的源代码
% godoc -src sync Mutex
显示如下:
// A Mutex is a mutual exclusion lock. // Mutexes can be created as
part of other structures // the zero value for a Mutex is an unlocked
mutex. type Mutex struct {
state int32
sema uint32 }// Local per-P Pool appendix. type poolLocal struct {
Mutex // Protects shared.
// contains filtered or unexported fields }
可以看到,显示了 unexported state! 便于我们对源代码进行深入的探索.
Mock out the file system (模仿文件系统)
现在有一个 package,这个包需要和 file system 进行协作,但是你不想让你的测试使用真正的磁盘,应该怎么办?
var fs fileSystem = osFS{}
type fileSystem interface { //应该是标准库中文件系统的接口
Open(name string) (file,error)
Stat(name string) (os.fileInfo,error)
}
type file interface { //标准库 中 file的接口
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo,error)
}
type osFs struct{} // osFs 类型,实现了 fileSystem 接口。
func (osFs) Open(name string) (file,error) //只要它的返回值,实现file接口即可
func (osFs) Stat(name string) (os.FileInfo,error)
Method expression (方法表达式)
type T struct{} //定义了一个新的类型 T
func (T) Foo(string) {fmt.Println(s)} //定义了这个类型T的一个方法
//fn 是一个函数类型的变量,将这个方法赋值给这个变量
// 值得注意的是: fn 的类型是 func(T,string)
// Foo 函数的类型是: func (T) Foo(string) 类型
var fn func(T,sring) = T.Foo
os/exec 中的一个真实的例子:
func (c *Cmd) stdin() (f *os.File,error)
func (c *Cmd) stdout() (f *os.File,error)
func (c *Cmd) stderr() (f *os.File,error)
type F func(*cmd) (*os.File,error)
for _,setupFd := range []F{(*Cmd).stdin,(*Cmd).stdout,(*Cmd).stderr} { //定义了一个切片
fd,err := steupFd(c)
if err != nil {
c.closeDescriptors(c.closeAfterStart)
c.closeDescriptors(c.closeAfterWait)
return err
}
c.childFiles = append(c.childFiles,fd)
}
Send and receive on the same channel
package main
import "fmt"
var battle = make(chan string) //定义一个 channel 变量
func warrior(name string,done chan struct{}) {
//现在问题来了:同一个select中,同一个 channel的情况应该如何应对?
select {
case oppoent := <-battle: //battle 接受数据
fmt.Printf("%s beat %s\n",name,oppoent)
case battle <- name: //battle 发送数据
// I lost
}
done <- struct{}{}
}
func main() {
done := make(chan struct{})
langs := []string{"Go","C","C++","Java","Perl","Python"}
for _,s := range langs {
go warrior(s,done) //生成多个 Goroutine
}
for _ = range langs {
<-done //等待 Goroutine结束,什么时候整个进程结束?
}
}
多次运行程序,输出并不一样:
第一次运行:
Java beat C++
Go beat C
Perl beat Python
第二次运行:
Python beat Perl
Go beat C
C++ beat Java
现在问题是:
1. 在同一个Select中,如果同时又两个 或者两个 以上的 case 语句中: 同样一个 channel 进行接收数据 会怎么样?
2. 同样一个 channel 同时发送数据,会怎么样?
3. 同样一个 channel (发送 和 接收 ) 数据,会怎么样? 会有自发自收的现象吗?
自己的猜测:
1. 如果有两个 channel 同时发送数据,会随机选择一个 channel 进行发送数据@H_502_336@
2. 如果有两个 channel 同时接收数据,会随机选择一个 channel 进行 接收数据@H_502_336@。
3. 同一个 channel (发送和接收) 数据,不会生成自发自收现象, 将会阻塞在这个 channel 中。
对于上述的例子, 具体Select是如何工作的,不太懂?(求高手指教)
Using close to broadcast (使用 close 进行广播)
package main
import (
"fmt"
"math/rand" //这样子写
"time"
)
func waiter(i int,block,done chan struct{}) {
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
fmt.Println(i,"waiting...")
<-block //所有的 goroutine 会阻塞在 block这里,直到 close(block)
fmt.Println(i,"done!")
done <- struct{}{}
}
func main() {
block,done := make(chan struct{}),make(chan struct{})
for i := 0; i < 4; i++ {
go waiter(i,done)
}
time.Sleep(5 * time.Second)
close(block) //关闭的时候,所有阻塞的 block 将会停止阻塞 (相当于进行了一次广播)
for i := 0; i < 4; i++ {
<-done
}
}
程序输出如下: 3 waiting... 2 waiting... 1 waiting... 0 waiting... 3 done! 2 done! 1 done! 0 done!
- 首先 会让所有的goroutine 阻塞 在 “<-block这里”
- 当main 的Goroutine 进行 close(block)的时候,所有的block 就不会再次阻塞了
- 相当于 使用 close() 进行了一次广播
NIl channel in select
func worker(i int,ch chan work,quit chan struct{}) {
for {
select {
case w :=<- ch:
if quit == nil { //一个 channel == nil ?
w.Refuse();
fmt.Println("worker",i,"refused",w)
break;
}
w.Do();
case <-quit:
fmt.Println("worker","quiting")
quit = nil (赋值成 nil)
}
}
}
func main() {
ch,quit := make(chan work),make(chan struct{})
go makeWork(ch)
for i := 0; i < 4; i++ {
go worker(i,ch,quit)
}
time.Sleep(5 * time.Second)
close(quit)
time.Sleep(2 * time.Second)
}
上述代码有些不太懂得地方。
1. 当一个 close(quit),之后,quit就从一个正常的 channel 变成了 nil 了吗? (应该不是)
2. 如果 当没有 quit 没有返回的时候,工人永远在干活。
3. 当 quit 返回了自后,quit = nil,然后接下来如果要命令工人干活,那么工人就会退出不干了。
总结
总体来说,比较难的地方,在于 channel 和 struct。 1. channel 的各种利用,来实现同步 和异步,并发等功能。只能在代码中进行探索了。 2. 有关 golang 的内存模型这些东西,不是很理解,很多地方不明不白的。只能等到以后慢慢理解了。