Go 語言中的併發能夠用兩種方式實現:編程
第一種方式,支持順序通訊進程(communicating sequential processes),簡稱 CSP。CSP是一種現代的併發編程模型,在這種編程模型中值會在不一樣的運行實例(goroutine)中傳遞,儘管大多數狀況下仍然是被限制在單一實例中。服務器
第二種實現方式就是更爲傳統的併發模型:多線程共享內存。網絡
在Go語言中,每個併發的執行單元叫做一個goroutine。當一個程序啓動時,其主函數即在一個單獨的goroutine中運行,咱們叫它main goroutine。新的goroutine會用go語句來建立。在語法上,go語句是一個普通的函數或方法調用前加上關鍵字go。go語句會使其語句中的函數在一個新建立的goroutine中運行。而go語句自己會迅速地完成。主goroutine 結束運行,則 後臺goroutine結束執行。多線程
示例1併發
主 goroutine和後臺goroutinetcp
func main() { go spinner(100 * time.Millisecond) const n = 45 fibN := fib(n) // slow fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) } //旋轉的動畫 func spinner(delay time.Duration) { for { for _, r := range `-\|/` { fmt.Printf("\r%c", r) time.Sleep(delay) } } } //菲波那契數列 func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) }
示例2函數
下面的例子是順序執行的時鐘服務器,它會每隔一秒鐘將當前時間寫到客戶端動畫
package main import ( "log" "net" "time" "io" ) func main() { listener, err := net.Listen("tcp", "localhost:8000") if err != nil { log.Fatal(err) } for { conn, err := listener.Accept() if err != nil { log.Print(err) // e.g., connection aborted continue } handleConn(conn) // handle one connection at a time } } func handleConn(c net.Conn) { defer c.Close() for { _, err := io.WriteString(c, time.Now().Format("15:04:05\n")) if err != nil { return // e.g., client disconnected } time.Sleep(1 * time.Second) } }
分析:線程
net.Listen函數建立了一個net.Listener的對象,這個對象會監聽一個網絡端口上到來的鏈接,在這個例子裏咱們用的是TCP的localhost:8000端口。listener對象的Accept方法會直接阻塞,直到一個新的鏈接被建立,而後會返回一個net.Conn對象來表示這個鏈接。orm
能夠使用 netcat命令鏈接這個服務。
或者使用 net.Dial() 來鏈接這個服務
// This is a read-only TCP client. package main import ( "io" "log" "net" "os" ) func main() { conn, err := net.Dial("tcp", "localhost:8000") if err != nil { log.Fatal(err) } defer conn.Close() mustCopy(os.Stdout, conn) } func mustCopy(dst io.Writer, src io.Reader) { if _, err := io.Copy(dst, src); err != nil { log.Fatal(err) } }
net.Dial() 撥號,返回一個鏈接。從鏈接中獲取數據打印到輸出流。
併發分析:
上面的服務端同時只能處理一個客戶端鏈接,客戶端必須等服務端完成工做才執行。爲了支持併發,在handleConn函數調用的地方增長go關鍵字,讓每一次handleConn的調用都進入一個獨立的goroutine。
示例3
併發的 echo服務。在單個鏈接中創建多個 goroutine