go tour 看完了,接下来整理Effective go
 
类型参数(模板类) 在函数名后或者类型名后加中括号可以为其指定类型参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func  Index [T  comparable ](s []T, x T)   int  {	for  i, v := range  s { 		 		 		if  v == x { 			return  i 		} 	} 	return  -1  } func  main ()   {	 	si := []int {10 , 20 , 15 , -10 } 	fmt.Println(Index(si, 15 )) 	 	ss := []string {"foo" , "bar" , "baz" } 	fmt.Println(Index(ss, "hello" )) } 
 
1 2 3 4 type  List[T any] struct  {	next *List[T] 	val  T } 
 
协程 使用go语句来启动一个新的协程
1 2 3 4 5 6 7 8 9 10 11 func  say (s string )   {	for  i := 0 ; i < 5 ; i++ { 		time.Sleep(100  * time.Millisecond) 		fmt.Println(s) 	} } func  main ()   {	go  say("world" ) 	say("hello" ) } 
 
启动协程时,参数的求值在当前协程中,函数的执行在新协程中,不同协程共享同样的内存空间
信道 信道用于在协程中进行通信,相比sync库而言信道是一种更轻量化的同步手段,信道分为带缓冲和无缓冲两种 信道由make(chan T,size)语句创建,当size为0是就是无缓冲的信道 对于无缓冲信道的发送端而言,在写入数据后会先检查是否有其他协程在等待数据,如果有则直接转移数据并继续执行,否则阻塞,对于接收端而言,在接收到数据前都会阻塞,接收到后执行,此处的发送和接收都是单次事件,即接收/发送一个数据(比如一个值)后就会解除阻塞 对于带缓冲信道的发送端,如果缓存已满则阻塞,否则不断往缓存中写数据,对于接收端,如果缓存中有数据则从中间取一个数据并继续执行,否则阻塞
range/close close语句用于永久关闭一个信道,被关闭的信道,被关闭的信道不能写入新数据,缓冲区中已有的数据读取完后就只能读取到零值,可以向<-语句添加第二个参数检查信道是否已经关闭v, ok := <-ch
for range循环可以用于不断从信道中接收值,直到信道关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func  fibonacci (n int , c chan  int )   {	x, y := 0 , 1  	for  i := 0 ; i < n; i++ { 		c <- x 		x, y = y, x+y 	} 	close (c) } func  main ()   {	c := make (chan  int , 10 ) 	go  fibonacci(cap (c), c) 	for  i := range  c { 		fmt.Println(i) 	} } 
 
关于close有两个原则,一是信道并不总是需要关闭,只有当我们想通知接收方停止接收数据时我们才需要关闭信道,二是信道总是应该由发送者关闭,因为向已关闭的信道发送数据会导致panic,这里的信号其实是单向传递的
select select可以用于等待多个协程,其会阻塞到某个分支可以继续执行,有点类似协程版的switch,如果有多个分支可以继续执行则会 随机  选择一个分支执行(go tour是这么说的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func  fibonacci (c, quit chan  int )   {	x, y := 0 , 1  	for  { 		select  { 		case  c <- x: 			x, y = y, x+y 		case  <-quit: 			fmt.Println("quit" ) 			return  		} 	} } func  main ()   {	c := make (chan  int ) 	quit := make (chan  int ) 	go  func ()   { 		for  i := 0 ; i < 10 ; i++ { 			fmt.Println(<-c) 		} 		quit <- 0  	}() 	fibonacci(c, quit) } 
 
select也支持default分支,当没有分支能执行但又不想阻塞时就可以添加default分支执行
Mutex sync.Mutex是go中最基本的互斥锁,当我们只想进行同步而不转移数据时可以利用Mutex的Lock和Unlock方法手动对协程上锁,一个技巧是使用defer调用Unlock来保证协程一定会被解锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 type  SafeCounter struct  {	mu sync.Mutex 	v  map [string ]int  } func  (c *SafeCounter)   Inc(key string ) {	c.mu.Lock() 	 	c.v[key]++ 	c.mu.Unlock() } func  (c *SafeCounter)   Value(key string ) int  {	c.mu.Lock() 	 	defer  c.mu.Unlock() 	return  c.v[key] } func  main ()   {	c := SafeCounter{v: make (map [string ]int )} 	for  i := 0 ; i < 1000 ; i++ { 		go  c.Inc("somekey" ) 	} 	time.Sleep(time.Second) 	fmt.Println(c.Value("somekey" )) }