本文翻译 从 文章进行翻译,在此表示感谢
data := struct {
Title string
Users []*User //猜测 User 是一个接口,接口指针的切片
} {
title,
USERS,
}
err := tmpl.Execute(w, data)
(Cheaper and safer than using map[string]interface{})
确实没有太理解,是什么意思?
var hits struct{
sync.Mutex //匿名对象的方法,(类似于一种继承的方式)
n int
}
hits.Lock() // (和C++中的继承很类似)
hits.n++
hits.Unlock()
{"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 数据的解析和发送。
% 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.
% 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! 便于我们对源代码进行深入的探索.
现在有一个 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)
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)
}
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 进行发送数据
2. 如果有两个 channel 同时接收数据,会随机选择一个 channel 进行 接收数据。
3. 同一个 channel (发送和接收) 数据,不会生成自发自收现象, 将会阻塞在这个 channel 中。
对于上述的例子, 具体Select是如何工作的,不太懂?(求高手指教)
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, block, 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!
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", i, "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 的内存模型这些东西,不是很理解,很多地方不明不白的。只能等到以后慢慢理解了。