Go的logo是一隻萌萌的囊地鼠(Gopher)
當Go程序出現錯誤,程序將報panic(恐慌)
因此是錯誤代碼嚇到小地鼠了嗎哈哈
而後須要用recover來安撫它
相似於 try-catch的語法html
func b() { defer func() { //defer must declare before panic err:=recover() //defer + recover(),recover panic if err !=nil { fmt.Println("func b err") } }() panic("panic in func b") //panic 觸發結束程序 }
很是方便的併發操做,
一個goroutine對應一個函數編程
func hello() { fmt.Println("Hello World!") wg.Done() //goroutine done,count-1 } //替代time.Sleep(),確保goroutine都執行完畢 var wg sync.WaitGroup func main() { wg.Add(1) //count+1 for i := 0; i < 1000; i++ { wg.Add(1) go func(i int) { fmt.Println("hello",i) //造成了閉包,i公用 wg.Done() }(i) } //多線程執行數不一樣 go hello() //open a goroutine fmt.Println("hello world") //time.Sleep(time.Second) wg.Wait() //all goroutine done }
由於不一樣goroutine可能會同時讀寫同一個資源,因此須要給資源加鎖網絡
import "sync" var ( x int wg. sync.WaitGroup lock sync.Mutex ) func add() { for i := 0; i < 5000; i++ { lock.Lock() //將x值加鎖 x ++ lock.Unlock() //執行完將x的鎖取消 } wg.Done() }
var ( xx int64 rwlock sync.RWMutex //讀寫互斥鎖 ) func read() { rwlock.RLock() //加讀鎖 time.Sleep(time.Millisecond) rwlock.RUnlock() wg.Done() } func write() { rwlock.Lock() //寫加讀寫鎖 xx = xx + 1 time.Sleep(time.Millisecond*10) rwlock.Unlock() wg.Done() }
Go語言的併發模型是CSP(Communicating Sequential Processes),提倡經過通訊共享內存而不是經過共享內存而實現通訊。
一個goroutine的值經過channel發送給另外一個goroutine
通道FIFO(First in,First out)多線程
//ch1<- func f1(ch chan<- int) { //(單向通道)chan<- 表示只能發送到通道 for i := 0; i < 100; i++ { ch <- i } close(ch) } //( ch2 <- ch1 )^2 func f2(ch1 <-chan int,ch2 chan<- int) { //<-chan 表示只能從通道讀取 for { tmp,ok := <- ch1 if !ok { break } ch2 <- tmp*tmp } close(ch2) } func main() { ch1 := make(chan int,100) //初始化通道,100爲緩衝區大小 ch2 := make(chan int,200) go f1(ch1) go f2(ch1,ch2) for val := range ch2 { fmt.Println(val) } }
server:1.監聽端口 2.創建與client的連接 3.與之交互
client:1.創建與server的連接 2.發送信息 3.關閉鏈接閉包
//tcp_server_demo func process(conn net.Conn) { defer conn.Close() //close connection //data for { reader := bufio.NewReader(conn) //read message var buf [128]byte n,err := reader.Read(buf[:]) if err != nil { fmt.Println("read err,err:",err) break } recv := string(buf[:n]) fmt.Printf("get:%v\n",recv) //show message conn.Write([]byte("ok")) //reply } } func main() { //listen listen,err := net.Listen("tcp","127.0.0.1:20000") if err != nil { fmt.Printf("listen port failed,err:%v\n",err) return } //waiting for connection for { conn,err := listen.Accept() if err != nil { fmt.Printf("accept failed.err:%v\n",err) continue } //go! go process(conn) } }
//tcp_client_demo func main() { //conn conn,err := net.Dial("tcp","127.0.0.1:20000") if err != nil { fmt.Println("dial failed,err:",err) return } //send and receiver input := bufio.NewReader(os.Stdin) for { s,_ := input.ReadString('\n') s = strings.TrimSpace(s) if strings.ToUpper(s) == "q" { //q for quit return } //send message _,err := conn.Write([]byte(s)) if err !=nil { fmt.Printf("send failed,err:%v\n",err) return } //receiver var buf [1024]byte n,err := conn.Read(buf[:]) if err != nil { fmt.Printf("read failed,err:%v\n",err) return } fmt.Println("get reply: ",string(buf[:n])) } }
客戶端發送,服務端接收併發送反饋信息
併發
這裏要先運行server端,否則client端找不到端口會恐慌的
tcp
執行go test來測試功能是否符合預期函數
func TestSplit(t *testing.T) { //測試哪一個函數就叫Testxxx() type test struct { input string sep string want []string } tests := map[string]test { //給出幾組樣例,OJ或者leetcode不會就是這樣的吧.. "simple":test {"ab c"," ",[]string{"ab","c"}}, //給測試數據命令,能夠針對這組數據進行測試 "multi sep":test{"hello,World",",",[]string{"hello","World"}}, } for name,tc := range tests { t.Run(name, func(t *testing.T) { got := Split(tc.input,tc.sep) if !reflect.DeepEqual(got,tc.want) { t.Errorf("name:%s want:%v got:%v\n",name,tc.want,got) } }) } }
經過測試:
未能經過測試:
性能
性能測試,將跑夠足夠的量來測試單元測試
//性能基準測試 func BenchmarkSplit(b *testing.B) { //b.N 由go test決定是否繼續加大測試量 for i := 0; i < b.N; i++ { Split("a,v,c",",") } }
將給出詳細的測試結果:
Go的基礎語法到這裏就粗略的過一遍了,若是要打磨本身的基礎的,能夠從學校OJ一直到acm題多加練習。代碼量上去了,理解也會水漲船高。
我可能沒有將Go基礎語法所有內容寫出來,並且我能夠說只是複製粘貼稍微整理了一下代碼,
更加詳細的教學請移步大佬的博客李文周的博客
再次感謝前輩的教學內容。
挺看好Go的前景的,能從Go的各方各面感受到這個語言的蓬勃生氣,
但可能由於仍是初學者,眼界不夠,暫時也說不出個子醜寅卯來,繼續學吧。