上篇3D 視角看 Go 併發編程
視頻連接+PPT資料以下
連接:https://pan.baidu.com/s/1yaZp7ITQq_o01OBuGGlzwQ 密碼:f3cmcss
Go 語言中並無子類繼承這樣的概念,而是經過嵌入(Embedding)的方式來實現類或者接口的組合。git
// 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 語法:github
// 普通函數 func doStuff(s string) { } func main() { // 使用命名函數建立 Goroutine go doStuff("foobar") // 使用匿名內部函數建立 Goroutine go func (x int) { // function body goes here }(42) }
信道(Channel)是帶有類型的管道,能夠用於在不一樣的 Goroutine 之間傳遞消息,其基礎操做以下:golang
// 建立類型爲 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),其讀取方在信道被寫滿前都不會被阻塞:segmentfault
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 關鍵字,用於等待多個信道的執行結果:app
// 建立兩個信道 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 項目,其目錄組織方式以下:ide
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
查看原文