上篇3D 视角看 Go 并发编程
视频链接+PPT资料如下
链接:https://pan.baidu.com/s/1yaZp7ITQq_o01OBuGGlzwQ 密码:f3cm
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 是轻量级的线程,可以参考并发编程导论一文中的进程、线程与协程的讨论;Go 为我们提供了非常便捷的 Goroutines 语法:
// 普通函数
func doStuff(s string) {
}
func main() {
// 使用命名函数创建 Goroutine
go doStuff("foobar")
// 使用匿名内部函数创建 Goroutine
go func (x int) {
// function body goes here
}(42)
}
信道(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)
}
}
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 官方推荐的 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"
}
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