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的各方各面感受到这个语言的蓬勃生气,
但可能由于仍是初学者,眼界不够,暂时也说不出个子丑寅卯来,继续学吧。