Go 语法速览与实践清单(下-V0.5)

龚志文
2023-12-01

上篇3D 视角看 Go 并发编程
视频链接+PPT资料如下
链接:https://pan.baidu.com/s/1yaZp7ITQq_o01OBuGGlzwQ 密码:f3cm

Embedding

Go 语言中并没有子类继承这样的概念,而是通过嵌入(Embedding)的方式来实现类或者接口的组合。

// ReadWriter 的实现需要同时满足 Reader 与 Writer
type ReadWriter interface {
    Reader
    Writer
}

// Server 暴露了所有 Logger 结构体的方法
type Server struct {
    Host string
    Port int
    *log.Logger
}

// 初始化方式并未受影响
server := &Server{"localhost", 80, log.New(...)}

// 却可以直接调用内嵌结构体的方法,等价于 server.Logger.Log(...)
server.Log(...)

// 内嵌结构体的名词即是类型名
var logger *log.Logger = server.Logger

并发编程

Goroutines

Goroutines 是轻量级的线程,可以参考并发编程导论一文中的进程、线程与协程的讨论;Go 为我们提供了非常便捷的 Goroutines 语法:

// 普通函数
func doStuff(s string) {
}

func main() {
    // 使用命名函数创建 Goroutine
    go doStuff("foobar")

    // 使用匿名内部函数创建 Goroutine
    go func (x int) {
        // function body goes here
    }(42)
}

Channels

信道(Channel)是带有类型的管道,可以用于在不同的 Goroutine 之间传递消息,其基础操作如下:

// 创建类型为 int 的信道
ch := make(chan int)

// 向信道中发送值
ch <- 42

// 从信道中获取值
v := <-ch

// 读取,并且判断其是否关闭
v, ok := <-ch

// 读取信道,直至其关闭
for i := range ch {
    fmt.Println(i)
}

譬如我们可以在主线程中等待来自 Goroutine 的消息,并且输出:

// 创建信道
messages := make(chan string)

// 执行 Goroutine
go func() { messages <- "ping" }()

// 阻塞,并且等待消息
msg := <-messages

// 使用信道进行并发地计算,并且阻塞等待结果
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 c 中接收

如上创建的是无缓冲型信道(Non-buffered Channels),其是阻塞型信道;当没有值时读取方会持续阻塞,而写入方则是在无读取时阻塞。我们可以创建缓冲型信道(Buffered Channel),其读取方在信道被写满前都不会被阻塞:

ch := make(chan int, 100)

// 发送方也可以主动关闭信道
close(ch)

Channel 同样可以作为函数参数,并且我们可以显式声明其是用于发送信息还是接收信息,从而增加程序的类型安全度:

// ping 函数用于发送信息
func ping(pings chan<- string, msg string) {
    pings <- msg
}

// pong 函数用于从某个信道中接收信息,然后发送到另一个信道中
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

同步

同步,是并发编程中的常见需求,这里我们可以使用 Channel 的阻塞特性来实现 Goroutine 之间的同步:

func worker(done chan bool) {
    time.Sleep(time.Second)
    done <- true
}

func main() {
    done := make(chan bool, 1)
    go worker(done)

    // 阻塞直到接收到消息
    <-done
}

Go 还为我们提供了 select 关键字,用于等待多个信道的执行结果:

// 创建两个信道
c1 := make(chan string)
c2 := make(chan string)

// 每个信道会以不同时延输出不同值
go func() {
    time.Sleep(1 * time.Second)
    c1 <- "one"
}()
go func() {
    time.Sleep(2 * time.Second)
    c2 <- "two"
}()

// 使用 select 来同时等待两个信道的执行结果
for i := 0; i < 2; i++ {
    select {
    case msg1 := <-c1:
        fmt.Println("received", msg1)
    case msg2 := <-c2:
        fmt.Println("received", msg2)
    }
}

Web 编程

HTTP Server

package main

import (
    "fmt"
    "net/http"
)

// define a type for the response
type Hello struct{}

// let that type implement the ServeHTTP method (defined in interface http.Handler)
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello!")
}

func main() {
    var h Hello
    http.ListenAndServe("localhost:4000", h)
}

// Here's the method signature of http.ServeHTTP:
// type Handler interface {
//     ServeHTTP(w http.ResponseWriter, r *http.Request)
// }

Beego

利用 Beego 官方推荐的 bee 命令行工具,我们可以快速创建 Beego 项目,其目录组织方式如下:

quickstart
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
├── tests
│   └── default_test.go
└── views
    └── index.tpl

在 main.go 文件中,我们可以启动 Beego 实例,并且调用路由的初始化配置文件:

package main

import (
        _ "quickstart/routers"
        "github.com/astaxie/beego"
)

func main() {
        beego.Run()
}

而在路由的初始化函数中,我们会声明各个路由与控制器之间的映射关系:

package routers

import (
        "quickstart/controllers"
        "github.com/astaxie/beego"
)

func init() {
        beego.Router("/", &controllers.MainController{})
}

也可以手动指定 Beego 项目中的静态资源映射:

beego.SetStaticPath("/down1", "download1")
beego.SetStaticPath("/down2", "download2")

在具体的控制器中,可以设置返回数据,或者关联的模板名:

package controllers

import (
        "github.com/astaxie/beego"
)

type MainController struct {
        beego.Controller
}

func (this *MainController) Get() {
        this.Data["Website"] = "beego.me"
        this.Data["Email"] = "astaxie@gmail.com"
        this.TplNames = "index.tpl" // version 1.6 use this.TplName = "index.tpl"
}

DevPractics: 开发实践

文件读写

import (
    "io/ioutil"
)
...
datFile1, errFile1 := ioutil.ReadFile("file1")
if errFile1 != nil {
    panic(errFile1)
}
...

测试

VSCode 可以为函数自动生成基础测试用例,并且提供了方便的用例执行与调试的功能。

/** 交换函数 */
func swap(x *int, y *int) {
    x, y = y, x
}

/** 自动生成的测试函数 */
func Test_swap(t *testing.T) {
    type args struct {
        x *int
        y *int
    }
    tests := []struct {
        name string
        args args
    }{
        // TODO: Add test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            swap(tt.args.x, tt.args.y)
        })
    }
}

转载|Segmentfault
感谢作者:王下邀月熊_Chevalier
查看原文

转载于:https://blog.51cto.com/51reboot/2095690

 类似资料: