day03 go中map及函数


切片 == 列表 map == 字典类型 线程不安全 要变成安全的就需要加锁 具体set.go 太麻烦 提供了更加简便的方法 互斥锁 HcMutex.Lock() HcMutex.UnLock() 同一时间只能一个执行 获取到锁 读写锁 RLock() unRLock() 写锁 Lock() UnLock()

package main

import (
"fmt"
"sync"
"time"
)

// 为了解决map线程不安全 ,我们自己加锁

type concurrentMap struct {
   mp map[int]int
   sync.RWMutex
}

// 通过set 方法做原有map的赋值 m[key] =v
func (c *concurrentMap) Set(key, value int) {
   // 加写锁
   c.Lock()
   c.mp[key] = value
   c.Unlock()

}

// 通过get 方法做原有map的读取值操作 v:= m[key]
func (c *concurrentMap) Get(key int) int {
   //先获取读锁
   c.RLock()
   res := c.mp[key]
   c.RUnlock()
   return res
}

func main() {
   c := concurrentMap{
      mp: make(map[int]int),
   }
   // 一个线程循环写map
   go func() {
      for i := 0; i < 10000; i++ {
         c.Set(i, i)
      }
   }()
   // 一个线程循环读map
   go func() {
      for i := 0; i < 10000; i++ {
         res := c.Get(i)
         fmt.Printf("[cmap.get][%d=%d]\n", i, res)
      }
   }()
   time.Sleep(1 * time.Hour)

}


m := sync.map{}
package main

import (
   "fmt"
   "log"
   "strings"
   "sync"
)

func main() {

   m := sync.Map{}
   // 新增
   for i := 0; i < 10; i++ {
      key := fmt.Sprintf("key_%d", i)
      m.Store(key, i)
   }
   // 删除
   m.Delete("key_8")

   // 改m.Store
   m.Store("key_9", 999)

   // 查询
   res, loaded := m.Load("key_09")
   if loaded {
      //  类型断言 res.(int)
      log.Printf("[key_09存在 :%v 数字类型:%d]", res, res.(int))
   }

   // 遍历 return false 停止
   m.Range(func(key, value interface{}) bool {
      k := key.(string)
      v := value.(int)
      if strings.HasSuffix(k, "3") {
         log.Printf("不想要3")
         //return true
         return false
      } else {
         log.Printf("[sync.map.Range][遍历][key:=%s][v:=%d]", k, v)

         return true
      }

   })
   // LoadAndDelete 先获取值再删掉
   s1, loaded := m.LoadAndDelete("key_7")
   log.Printf("key_7 LoadAndDelete :%v", s1)
   s2, loaded := m.Load("key_7")
   log.Printf("key_7 LoadAndDelete:%v", s2)

   actual, loaded := m.LoadOrStore("key_8", 158)
   if loaded {
      log.Printf("key_8原来的值是:%v", actual)
   } else {
      log.Printf("key_8原来没有,实际是:%v", actual)
   }

   actual, loaded = m.LoadOrStore("key_1", 158)
   if loaded {
      log.Printf("key_1原来的值是:%v", actual)
   } else {
      log.Printf("key_1原来没有,实际是:%v", actual)
   }
}

PS:golang 连接跳转到其他文章,Markdown的方式引用跳转 使用markdown语法:点击跳转

  • 分片,多分片性能更高,互不影响 每个走自己的锁,减少加锁时间
package main

import (
   "fmt"
   "github.com/orcaman/concurrent-map"
   "log"
   "time"
)

func main() {
   // Create a new map.
   m := cmap.New()

   // 循环写map
   go func() {

      for i := 0; i < 10000; i++ {
         key := fmt.Sprintf("key_%d", i)
         m.Set(key, i)
      }

   }()
   // 循环读map
   go func() {

      for i := 0; i < 10000; i++ {
         key := fmt.Sprintf("key_%d", i)
         v, exists := m.Get(key)
         if exists {
            log.Printf("[%s=%v]", key, v)
         }
      }

   }()
   // 循环写map
   go func() {

      for i := 0; i < 10000; i++ {
         key := fmt.Sprintf("key_%d", i)
         m.Set(key, i)
      }

   }()
   // 循环写map
   go func() {

      for i := 0; i < 10000; i++ {
         key := fmt.Sprintf("key_%d", i)
         m.Set(key, i)
      }

   }()
   // 循环写map
   go func() {

      for i := 0; i < 10000; i++ {
         key := fmt.Sprintf("key_%d", i)
         m.Set(key, i)
      }

   }()

   time.Sleep(1 * time.Hour)

}

带过期时间的缓存map

package main

import (
   "fmt"
   "log"
   "sync"
   "time"
)

//带过期时间的map 定时清理

type Cache struct {
   sync.RWMutex
   mp map[string]*item
}

type item struct {
   value int   //值
   ts    int64 // 时间戳,item 被创建出来的时间
}

func (c *Cache) Get(key string) *item {
   c.RLock()
   defer c.RUnlock()
   return c.mp[key]
}

func (c *Cache) CacheNum() int {
   c.RLock()

   keys := make([]string, 0)
   //i := 0
   for k, _ := range c.mp {
      //fmt.Println(k)
      keys = append(keys, k)
      //i++

   }
   c.RUnlock()
   return len(keys)
}

func (c *Cache) Set(key string, value *item) {
   c.Lock()
   defer c.Unlock()
   c.mp[key] = value
}

func (c *Cache) Clean(timeDelta int64) {
   // 每5秒执行一此清理
   for {
      now := time.Now().Unix()
      // 待删除的key的切片
      toDelKeys := make([]string, 0)

      // 先加读锁,把所有待删除的拿到
      c.RLock()
      for k, v := range c.mp {
         // 时间比较
         if now-v.ts > timeDelta {
            // 认为这个k,v过期了,
            // 不直接删除,为了降低加锁时间,加入待删除的切片
            toDelKeys = append(toDelKeys, k)
         }
      }
      c.RUnlock()

      // 加写锁 删除,降低加写锁的时间
      c.Lock()
      for _, k := range toDelKeys {
         log.Printf("[删除过期数据][key:%s]", k)
         delete(c.mp, k)
      }
      c.Unlock()
      // 写锁释放
      time.Sleep(2 * time.Second)
   }
}

func main() {
   c := Cache{
      mp: make(map[string]*item),
   }
   // 让清理的任务异步执行
   // 每5秒运行一次,检查时间差大于30秒item 就删除
   go c.Clean(30)

   // 从mysql中读取到了数据,塞入缓存
   for i := 0; i < 10; i++ {
      key := fmt.Sprintf("key_%d", i)
      ts := time.Now().Unix()
      im := &item{
         value: i,
         ts:    ts,
      }
      // 设置缓存
      log.Printf("[设置缓存][item][key:%s][v:%v]", key, im)
      c.Set(key, im)
   }
   log.Printf("缓存中的数据量:%d", c.CacheNum())
   time.Sleep(33 * time.Second)

   log.Printf("缓存中的数据量:%d", c.CacheNum())

   // 更新缓存
   for i := 0; i < 5; i++ {
      key := fmt.Sprintf("key_%d", i)
      ts := time.Now().Unix()
      im := &item{
         value: i,
         ts:    ts,
      }
      // 设置缓存
      log.Printf("[更新缓存][item][key:%s][v:%v]", key, im)
      c.Set(key, im)
   }
   log.Printf("缓存中的数据量:%d", c.CacheNum())
   select {}

}

开源cache

package main

import (
   "fmt"
   "github.com/patrickmn/go-cache"
   "time"
)

func main() {
   // Create a cache with a default expiration time of 5 minutes, and which
   // purges expired items every 10 minutes
   c := cache.New(30*time.Second, 5*time.Second)

   // eSet the value of the key "foo" to "bar", with the default xpiration time
   // 默认30s,执行5s一次
   c.Set("k1", "v1", 31*time.Second)
   res, ok := c.Get("k1")
   fmt.Println(res, ok)
   time.Sleep(time.Second * 32)
   res, ok = c.Get("k1")
   fmt.Println(res, ok)
}
解决模块导入失败的问题:
go env -w GOPROXY=https://goproxy.cn,direct
➜ ~ go env |grep GOPROXY
GOPROXY="https://goproxy.cn,direct"
➜ ~ go get cloud.google.com/go/compute/metadata

go env -w GO111MODULE=on

传统的 $GOPATH/src 现在已经不用了,然后就是 Go modules 的缓存虽然目前还暂时存放到 $GOPATH/pkg/mod 中,但是在未来就有可能移动到 $GOCACHE/mod 中了,所以建议你应该尽可能地摆脱 GOPATH 才对。如果老哥你感兴趣有时间想深入了解 Go modules 的话,建议老哥去看一下这周四我在【Go 夜读】做的一期比较深入的在线直播分享,包含了 Go modules 的由来之类的,

GoLand 在项目设置中应该叫“Go Modules (vgo)”的选项,把里面的“Proxy”设置为https://goproxy.cn就好了,如果你是 Go 1.13 的话就设置成https://goproxy.cn,direct。

实际解决: 01

在创建项目的时候可以指定: 02

在终端配置:

go env  
go env -w GO111MODULE=on
go env -w GOPROXY="https://goproxy.cn,direct" 
go env |grep GOPROXY

go get "github.com/orcaman/concurrent-map"  
go mod init 
go mod tidy

任务高并发

python
celery 队列 + rabbitmq 实现  之前 讲过直播day13 
go 
channel 类似celery  + goruning 实现

channel:

package main

import (
   "log"
   "time"
)

func main() {
   data := make(chan int)
   // 读取数据任务
   go func() {
      // 双变量形式
      for {
         if r, ok := <-data; ok {
            log.Printf("[接收到了数据,并开始处理]: %v", r)
         } else {
            log.Printf("chan关闭了")
            break
         }
      }
      // 单变量 
      //for r:=range data{
      // log.Printf("[接收到了数据,并开始处理]: %v", r)
      //}
      //log.Printf("chan关闭了")
      // 死循环 类似while
      //for {
      // r := <-data
      // log.Printf("[接收到了数据,并开始处理]: %v", r)
      //}
   }()
   // 写入数据
   data <- 1
   time.Sleep(2 * time.Second)
   data <- 2
   time.Sleep(2 * time.Second)
   data <- 3
   time.Sleep(2 * time.Second)
   // 现象是  chan 关闭了 没打印
   close(data)
   // 加入等待1s 发现 chan关闭了 打印了 等待其他进程执行完毕
   time.Sleep(1 * time.Second)
}

带缓冲区的chan 主线程不退出方式二:

package main

import (
   "log"
   "time"
)

func main() {
   // 初始化一个类型int的chan,缓冲区为3
   data := make(chan int,3)
   quit:=make(chan bool) // 达成和最后的time.sleep一样的效果  阻塞主线程,防止异步任务中有未处理完的就退出  类似Python 多线程join
   // 读取数据任务

   go func() {

      for d:=range data{
         log.Printf("[接收到了数据,并开始处理]: %v", d)
      }
      log.Printf("chan关闭了")
      // 本任务处理完了,告诉主线程可以退出了
      quit <- true

   }()
   // 写入数据
   data <- 1
   time.Sleep(2 * time.Second)
   data <- 2
   time.Sleep(2 * time.Second)
   data <- 3
   time.Sleep(2 * time.Second)
   data <- 4
   data <- 5
   // 现象是  chan 关闭了 没打印
   close(data)
   <-quit
   // 加入等待1s 发现 chan关闭了 打印了 等待其他进程执行完毕
   //time.Sleep(1 * time.Second)
}

channel 同步 异步

说明2个问题:

  • 1、quit 是等待所有子进程结束,发送一个结束的消息,主进程退出
  • 2、channel缓冲区,设置只能处理3个,同时只有三个在处理,只有处理完了其他任务才会进入处理
package main

import (
   "log"
   "time"
)

func main() {
   // 初始化一个类型int的chan,缓冲区为3
   data := make(chan int,3)
   quit:=make(chan bool) // 达成和最后的time.sleep一样的效果  阻塞主线程,防止异步任务中有未处理完的就退出  类似Python 多线程join
   // 读取数据任务

   go func() {

      for d:=range data{
         time.Sleep(2*time.Second)
         log.Printf("[接收到了数据,并开始处理]: %v", d)
      }
      log.Printf("chan关闭了,但是我还有清理工作,等我5秒钟")
      time.Sleep(5 * time.Second)
      // 本任务处理完了,告诉主线程可以退出了

      quit <- true

   }()
   // 写入数据
   data <- 1
   time.Sleep(2 * time.Second)
   data <- 2
   time.Sleep(2 * time.Second)
   data <- 3
   time.Sleep(2 * time.Second)
   data <- 4
   log.Printf("发布4")
   data <- 5
   log.Printf("发布5")
   data <- 6
   log.Printf("发布6")
   data <- 7
   log.Printf("发布7")
   data <- 8
   log.Printf("发布8")
   data <- 9
   log.Printf("发布9")
   data <- 10
   log.Printf("发布10")
   // 现象是  chan 关闭了 没打印
   close(data)
   <-quit
   log.Printf("真正推出了")
   // 加入等待1s 发现 chan关闭了 打印了 等待其他进程执行完毕
   //time.Sleep(1 * time.Second)
}

关闭的channel 返回是空值,bool类型就是false、int 0

01 close nil 的chan 02 向已关闭的chan 再次close 03 向已关闭的chan 再次写入值

package main

func main() {
   // panic: close of nil channel
   //var c1 chan int
   //close(c1)

   // panic: close of closed channel
   //c2 := make(chan int)
   //close(c2)
   //close(c2)

   // panic: send on closed channel
   c3 := make(chan int)
   close(c3)
   c3 <- 1
}
  • close(ch)关闭所有下游协程
  • 使用select处理多个channel
  • select可以同时监控多个通道的情况,只处理未阻塞的case。当通道为nil时,对应的case永远为阻塞,无论读写
package main

import (
   "log"
   "os"
   "os/signal"
   "syscall"
   "time"
)

var quitC = make(chan struct{})

func signalWork() {
   c := make(chan os.Signal, 1)
   signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
   // 当c中读取到值的时候,说明有人发了信号
   sig := <-c

   //通知所有读取 quitC的任务
   close(quitC)
   time.Sleep(2*time.Second)
   log.Printf("接收到了停止的信号,信号是:%v,pid=%d,要推出了", sig, os.Getpid())
}

func work01() {
   ticker := time.NewTicker(time.Second * 5)
   for {
      select {
      case <-ticker.C:
         log.Printf("[我是work01]【5秒周期到了】,干活")
      case <-quitC:
         log.Printf("[我是work01]【接收到主进程退出信号】,进行清理操作")
         return
      }
   }
}

func work02() {
   ticker := time.NewTicker(time.Second * 5)
   for {
      select {
      case <-ticker.C:
         log.Printf("[我是work02]【5秒周期到了】,干活")
      case <-quitC:
         log.Printf("[我是work02]【接收到主进程退出信号】,进行清理操作")
         return
      }
   }
}

func work03() {
   // 定时器,定时5秒执行
   ticker := time.NewTicker(time.Second * 5)
   for {
      select {
          // 当时间到了5秒钟 就执行,就是5秒执行一次 
      case <-ticker.C:
         log.Printf("[我是work03]【5秒周期到了】,干活")
      case <-quitC:
         log.Printf("[我是work03]【接收到主进程退出信号】,进行清理操作")
         return
      }
   }
}

func main() {
   go work01()
   go work02()
   go work03()
   signalWork()


}

流程控制 if多条件

package main

import "fmt"

func main() {
   x := 10
   y := "ok"
   if x == 10 && y == "ok" {
      fmt.Println("两个都满足")
   }else if x>10 || y=="ok"{
      fmt.Println("任意一个")

   }

   /*
   if x > 10 {

   } else if x == 10 {

   } else {

   }

    */
}

局部变量,变量作用域

package main

import (
   "fmt"
   "log"
)

func main() {
   m := map[string]string{
      "region": "bj",
      "idc":    "世纪互联",
   }
   if idc := m["idc"]; idc == "世纪互联" {
      log.Printf("机房:%v", idc)
   }
   idc1 := m["idc"]
   if idc1 == "世纪互联" {
      log.Printf("机房:%v", idc1)
   }

   //fmt.Println(idc)
   fmt.Println(idc1)


}

swith...case 用法

package main

import (
   "fmt"
)

func jj(s string){
   switch s {
   case "go":
      fmt.Println("go")
   case "python":
      fmt.Println("python")
   case "java":
      fmt.Println("java")

   default:
      fmt.Println("unkown")
   }
}

func main() {
   jj("py")
   jj("go")
   jj("java")


}

fallthrough 不重要:基本用不到,会继续往下面判断打印

package main

import (
   "fmt"
)

func jj(s string) {
   switch s {
   case "go":
      fmt.Println("go")
      // case接多个值
   case "python", "py":
      fmt.Println("python")
      // 继续判断 
      fallthrough
   case "java":
      fmt.Println("java")

   default:
      fmt.Println("unkown")
   }
}

func main() {
   jj("py")
   jj("go")
   jj("java")

}

循环控制 只有for Python中有for while 单条件判断

package main

import (
   "log"
)

func main() {

   var a,b int
   b = 10
   for a<b{
      log.Printf("我干活呢",a,b)
      a++
   }
   // 死循环
   //for {
   // log.Printf("我干活呢?")
   // time.Sleep(3 * time.Second)
   //
   //}

}

for 的三种用法:

package main

import "log"

func main() {

   for i:=0;i<5;i++{
      log.Printf("第一种 三段全的")
   }
   for i:=0;i<5;{
      log.Printf("第二种 自增写在里面")
      i++
   }
   var i int
   for ;i<5;{ // or for i<5  
      log.Printf("第三种 自增写在里面,初始化写在上面")
      i++
   }

}

切片、map(dict)通过for进行遍历:

package main

import "log"

func main() {
   s1:=[]int{10,20,30,40,50}
   m1:=map[string]string{"k1":"v1","k2":"v2","k3":"v3"}
   for index:=range s1{
      log.Printf("[切片]range遍历单变量,只有索引,[index:%v]",index)
   }
   for index,value:=range s1{
      log.Printf("【切片】range遍历双变量,[index:%v][value:%v]",index,value)
   }
   for key:=range m1{
      log.Printf("[map]range遍历单变量,只有key,[key:%v]",key)

   }
   for key,value:=range m1{
      log.Printf("[map]range遍历双变量,[key:%v][value:%v]",key,value)

   }


}
golang:
command +D 复制一行
command+shift+?多行注释
command+? 单行注释

切片是指针,而且要赋值,会有问题,解决 (for的内部实现机制会重用value )

package main

import "log"

func main() {
   a1 := make([]*int, 3)
   a2 := make([]int, 3)
   for k, v := range []int{1, 2, 3} {
       // 对v重新赋值可以解决
      v := v
      log.Printf("[v的值:%v][v的地址:%p]", v, &v)
      a1[k] = &v
      a2[k] = v

   }
   for i := range a1 {
      log.Printf("[指针切片的值为:%v]", *a1[i])

   }
   for i := range a2 {
      log.Printf("[指针切片的值为:%v]", a2[i])

   }

}

continue 和 break

package main

import (
   "fmt"
   "log"
   "strings"
)

func main() {
   // map无序遍历   有序 按照key去遍历==map中有讲 先通过切片 在通过切片再去遍历 
   m1 := make(map[string]string)
   for i := 0; i < 30; i++ {
      key := fmt.Sprintf("%d_key", i)
      value := fmt.Sprintf("%d_value", i)
      m1[key] = value
   }
   for k, v := range m1 {
      if strings.HasPrefix(k, "1") {
         log.Printf("[遇到1就continue]")
         continue
      }
      if k == "23_key" {
         log.Printf("[遇到23就break]")
         break
      }
      log.Printf("[正常处理数据【%v=%v】]", k, v)
   }

}

goto 下面例子会一直打印 一般不建议使用,少用goto

package main

import "log"

func main() {
   i:=0
   sum:
      log.Printf("[i=%d]",i)
      i++
      goto sum

}

打印100内值

package main

import "log"

func main() {
   i:=0
   sum:
      {
      log.Printf("[i=%d]",i)
      i++
      }
      if i<100{
      goto sum
      }
}

函数

  • go是强类型语言,需要指定返回值类型
package main

import (
   "fmt"
)

func max(n1, n2, int) int {
   if n1 > n2 {
      return n1
   }
   return n2
}

func main() {
   fmt.Println(max(1,10))
   fmt.Println(max(-1,2))

}

函数参数只是var声明,map要使用还需要make一下

package main

import "fmt"
// 返回参数命令 只是var 申明,使用前需要make一下 
func f1()(names []string,m map[string]int,num int){
   m = make(map[string]int)
   m["k1"] = 2
   return
}

func main() {
   a,b,c :=f1()
   fmt.Println(a,b,c)

}

03

变长参数:

package main

import "log"

// 变长参数 返回最小值
func min(a ...int)int{
   if len(a) == 0{
      return 0
   }
   min:=a[0]
   for _,v :=range a{
      if v<min{
         min =v
      }
   }
   return min
}

func main() {
   x1:=min(1,7,8,9,23,9)
   log.Printf("[直接传多个参数]:%d",x1)
   s1:=[]int{2,3,4,6,23,6,23}
   x2:=min(s1...)
   log.Printf("[数组传参]:%d",x2)

}
参数默认是值类型,copy传递
如果要引用传递,就传递指针
如果是引用类型 就是引用传递
package main

import (
   "log"
   "time"
)

// 值类型 值传递
func add1(num int) {
   log.Printf("[值传递][传入的参数值为:%d]", num)
   num++
   log.Printf("[值传递][add1计算后的值为:%d]", num)
}

// 值类型 引用传递
func add2(num *int) {
   log.Printf("[引用传递][传入的参数值为:%d]", *num)
   *num++
   log.Printf("[引用传递][add2计算后的值为:%d]", *num)
}

func main() {
   num := 1
   log.Printf("[局部遍历的值:%d]", num)
   add1(num)
   time.Sleep(1 * time.Second)
   log.Printf("[局部遍历的值:%d]", num)
   add2(&num)
   time.Sleep(1 * time.Second)
   log.Printf("[局部遍历的值:%d]", num)
}

map/切片都是引用传递:

package main

import (
   "log"
)

// 引用类型 引用传递
func mod(s1 []int, m1 map[string]string) {
   log.Printf("[引用传递][传入参数为:%v %v]", s1, m1)
   s1[0] = 100
   m1["a"] = "a2"
   log.Printf("[引用传递][函数内部处理完为:%v %v]", s1, m1)
}

func main() {
   s1 := []int{1, 2, 3}
   m1 := map[string]string{"a": "a1", "b": "b1"}
   mod(s1, m1)
   log.Printf("[引用传递][函数外部处理完为:%v %v]", s1, m1)
}

匿名函数 不带参数

package main

import "fmt"

func main() {

   f := func() {
      fmt.Println("abdc")
   }
   f()
   fmt.Printf("%T", f)

}

// 带参数 + 返回值的
func main() {


      f := func(args string) string {
         fmt.Println("abdc")
         return "abcd"
      }
      a:=f("abcd")
      fmt.Printf("%T,%v", f,a)

}

闭包准备:

package main

import (
   "fmt"
)

func FGen(x, y int) (func() int, func(int) int) {
   // 求和匿名函数
   sum := func() int {
      return x + y
   }
   // 求平均值的匿名函数
   arg:=func(z int)int{
      return (x+y)*z

   }
   return sum,arg
}

func main() {

   f1,f2:=FGen(1,2)
   fmt.Println(f1())
   fmt.Println(f2(3))

}

闭包函数 (函数嵌套) 不同闭包维护不同变量,跟其他语言闭包一样,都是内层函数调用外层变量,保持外层func不释放,内层执行完毕后才会释放外层函数 保持外层变量不释放

package main

import "fmt"

func Greeting()func(string)string{
   a:="你好啊"
   return func(s string) string {
      a+=s
      return a
   }
}

func main() {
   g1 := Greeting()
   g2 := Greeting()
   fmt.Println(g1("小姨"))
   fmt.Println(g1("李鬼"))
   fmt.Println(g1("松江"))
   fmt.Println(g2("拉ex"))


}

闭包做装饰器 增加扩展性,所有语言都是通用的,解耦,函数式编程比较重要的点:

package main

import (
   "log"
)

func add1() {
   sum := 0
   for i := 0; i < 10; i++ {
      sum += i
   }
   log.Printf("[普通的累加器]:%d", sum)
}

func add2() func(int) int {
   // 自由变量
   sum := 0
   return func(i int) int {
      sum += i
      return sum
   }
}

func callCF() {
   f := add2()
   sum := 0
   for i := 0; i < 10; i++ {
      sum = f(i)
      log.Printf("[闭包的累加器]:%d", sum)
   }
}

func main() {

   add1()

   // 闭包的
   callCF()
}

递归必须使用闭包,之前练习递归貌似没有使用闭包 是闭包,返回是一个函数,看来这里理解还不够

递归:斐波拉切数列

func fib(i int)int{
   if i==0||i==1{
      return i
   }
   return fib(i-1)+fib(i-2)
}

func main() {
   log.Println(fib(7))
   // 递归退出条件
   for i:=0;i<10;i++{
      log.Printf("[%d=%d]",i,fib(i))
   }
}

闭包 + 递归

package main

import "log"

// 实现1+..100
// 从低位加到高位
func sum1(num int)int{
   if num == 100{
      return num
   }
   return num + sum1(num+1)
}

// 从高位加到低位
func sum2(num int)int{
   if num == 1{
      return num
   }
   return num + sum2(num-1)
}

func main() {
   log.Printf("[从低位加到高位]:%d",sum1(1))
   log.Printf("[从高位加到低位]:%d",sum2(100))


}

defer 类似Python 上下文管理 with openxx as f enter exit

保证文件描述符被close

package main

import "fmt"

func main() {
   for i:=0;i<5;i++{
      // 后进先出 栈操作  
      defer fmt.Println(i)
   }


}

defer打印时间差

package main

import (
   "log"
   "time"
)

func main() {
   start := time.Now()
   log.Printf("开始时间为:%v", start)
   defer log.Printf("时间差:%v", time.Since(start))
   time.Sleep(3 * time.Second)
   log.Printf("函数结束")

}

解决:通过引用传递匿名函数解决:defer func

defer与return关系
值传递 深拷贝
return xxx 这条语句不是一条原子操作
- 先给返回值赋值
-return 1 要翻译成 res=1 +return  
- 调用defer语句
- 返回调用函数中

重点:
map 带过期时间、分片、sync.map
chan goroutine一起理解
函数

下一节内容:
panic和defer
结构体 == 类
面向对象
接口

https://github.com/orcaman/concurrent-map

MarkDown实现
生成目录的方法:
* [1.语法示例](#1)
 * [1.1图片](#1.1)
 * [1.2换行](#1.2)
 * [1.3强调](#1.3)
使用markdown语法:[点击跳转](#jump)