Let's GO(四)

人生苦短,Let's GO

今天我學了什麼?

1.panic && recover

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 觸發結束程序
}

2.goroutine(協程)

很是方便的併發操做,
一個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
}

3.鎖

由於不一樣goroutine可能會同時讀寫同一個資源,因此須要給資源加鎖網絡

a. 互斥鎖(sync.Mutex):控制共享資源只被一個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()
}

b.讀寫互斥鎖:適用於讀多寫少的狀況,速度更快。

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()
}

4.channel(通道)

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)
	}
}

5.網絡編程(tcp,udp)

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

6. 單元測試

執行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的各方各面感受到這個語言的蓬勃生氣,
但可能由於仍是初學者,眼界不夠,暫時也說不出個子醜寅卯來,繼續學吧。


人生苦短,GO!GO!GO!

相關文章
相關標籤/搜索