zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",err)
    		return
    	}
    	//waiting for connection
    	for {
    		conn,err := listen.Accept()
    		if err != nil {
    			fmt.Printf("accept failed.err:%v
    ",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('
    ')
    		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
    ",err)
    			return
    		}
    		//receiver
    		var buf [1024]byte
    		n,err := conn.Read(buf[:])
    		if err != nil {
    			fmt.Printf("read failed,err:%v
    ",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
    ",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!

  • 相关阅读:
    硬盘SSD、HDD和SSHD都是什么意思?哪种类型硬盘最好?
    记录vlookup的小问题
    找到两个字符串中相同的部分| 对字符串list后的奇妙发现
    jiayan:Cannot read model 'jiayan.klm' (utilfile.cc:74 in util::OpenReadOrThrow threw ErrnoException because `-1 == (ret = _open(name,
    windows下装kenlm
    每日一题力扣98 验证二叉搜索树
    每日一题力扣230 二叉搜索树中的第K小的元素
    每日一题力扣538 把二叉搜索树转换为累加树
    每日一题力扣530 二叉搜索树的最小绝对查
    每日一题力扣700 二叉搜索树中的搜索
  • 原文地址:https://www.cnblogs.com/syisyuan/p/12892266.html
Copyright © 2011-2022 走看看