Let's GO(四)

人生苦短,Let's GO

今天我学了什么?

1.panic && recover

Go的logo是一只萌萌的囊地鼠(Gopher)
当Go程序出现错误,程序将报panic(恐慌)
所以是错误代码吓到小地鼠了吗哈哈
然后需要用recover来安抚它
类似于 try-catch的语法

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端找不到端口会恐慌的
在这里插入图片描述

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!

posted @ 2020-05-14 23:58  随意随缘  阅读(280)  评论(0编辑  收藏  举报
有事您Q我