Golang -- 10件你不知道的事情

前端之家收集整理的这篇文章主要介绍了Golang -- 10件你不知道的事情前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文翻译 从 文章进行翻译,在此表示感谢

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!
  1. 首先 会让所有的goroutine 阻塞 在 “<-block这里”
  2. 当main 的Goroutine 进行 close(block)的时候,所有的block 就不会再次阻塞了
  3. 相当于 使用 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 的内存模型这些东西,不是很理解,很多地方不明不白的。只能等到以后慢慢理解了。

原文链接:https://www.f2er.com/go/190463.html

猜你在找的Go相关文章