语言基础

字符串 数组 切片

  • 数组的定义

    1. var a [3]int // 定义长度为3的int型数组, 元素全部为0
    2. var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3
    3. var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3
    4. var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6
  • 字符串 ```go //for range对字符串的迭代模拟实现 func forOnString(s string) { for i := 0; len(s) > 0; {

    1. r, size := utf8.DecodeRuneInString(s)
    2. fmt.Println(string(r),size,s)
    3. s = s[size:]
    4. i += size

    } }

  1. <a name="vh45M"></a>
  2. # Context
  3. ```go
  4. /*
  5. type Context interface {
  6. Deadline() (deadline time.Time, ok bool)
  7. Done() <-chan struct{}
  8. Err() error
  9. Value(key interface{}) interface{}
  10. }
  11. Deadline会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。
  12. Done方法返回一个信道(channel),当Context被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。
  13. 当Done信道关闭后,Err方法表明Context被撤的原因。
  14. Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁
  15. */

WithCancel

WithCancel创建的上下文需要主动调用cancel函数来结束上下文的生命周期

  1. func main() {
  2. // 父context(利用根context得到)
  3. ctx, cancel := context.WithCancel(context.Background())
  4. // 父context的子协程
  5. go watch1(ctx)
  6. // 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
  7. valueCtx, _ := context.WithCancel(ctx)
  8. // 子context的子协程
  9. go watch2(valueCtx)
  10. fmt.Println("现在开始等待3秒,time=", time.Now().Unix())
  11. time.Sleep(3 * time.Second)
  12. // 调用cancel()
  13. fmt.Println("等待3秒结束,调用cancel()函数")
  14. // 必须主动调用
  15. cancel()
  16. // 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
  17. time.Sleep(5 * time.Second)
  18. fmt.Println("最终结束,time=", time.Now().Unix())
  19. }
  20. // 父context的协程
  21. func watch1(ctx context.Context) {
  22. for {
  23. select {
  24. case <-ctx.Done(): //取出值即说明是结束信号
  25. fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
  26. return
  27. default:
  28. fmt.Println("父context的协程监控中,time=", time.Now().Unix())
  29. time.Sleep(1 * time.Second)
  30. }
  31. }
  32. }
  33. // 子context的协程
  34. func watch2(ctx context.Context) {
  35. for {
  36. select {
  37. case <-ctx.Done(): //取出值即说明是结束信号
  38. fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
  39. return
  40. default:
  41. fmt.Println("子context的协程监控中,time=", time.Now().Unix())
  42. time.Sleep(1 * time.Second)
  43. }
  44. }
  45. }

WithDeadline

WithDeadline 创建的上下文过期时间是指的某一个时刻,参数类型time.TIme可以看出,当父context到达过期时间时,所有由该父context创建的上下文全部过期

  1. func main() {
  2. // 父context(利用根context得到)
  3. ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
  4. defer cancel()
  5. // 父context的子协程
  6. go watch1(ctx)
  7. // 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
  8. valueCtx, _ := context.WithCancel(ctx)
  9. // 子context的子协程
  10. go watch2(valueCtx)
  11. // 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
  12. time.Sleep(10 * time.Second)
  13. fmt.Println("最终结束,time=", time.Now().Unix())
  14. }
  15. // 父context的协程
  16. func watch1(ctx context.Context) {
  17. for {
  18. select {
  19. case <-ctx.Done(): //取出值即说明是结束信号
  20. fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
  21. fmt.Println(ctx.Err().Error())
  22. return
  23. default:
  24. fmt.Println("父context的协程监控中,time=", time.Now().Unix())
  25. time.Sleep(1 * time.Second)
  26. }
  27. }
  28. }
  29. // 子context的协程
  30. func watch2(ctx context.Context) {
  31. for {
  32. select {
  33. case <-ctx.Done(): //取出值即说明是结束信号
  34. fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
  35. return
  36. default:
  37. fmt.Println("子context的协程监控中,time=", time.Now().Unix())
  38. time.Sleep(1 * time.Second)
  39. }
  40. }
  41. }

WithTimeout

WithTimeout 创建的上下文传入的时间参数可以理解为现在开始Context剩余的生命时长即指定总共的生命周期时长

  1. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

WithValue

用来和父context共享一些数据

  1. func main() {
  2. // 父context(利用根context得到)
  3. ctx := context.WithValue(context.Background(), "keys", "valus")
  4. // 父context的子协程
  5. go watch1(ctx)
  6. // 子context,注意:这里虽然也返回了cancel的函数对象,但是未使用
  7. valueCtx, _ := context.WithCancel(ctx)
  8. //// 子context的子协程
  9. go watch2(valueCtx)
  10. // 再等待5秒看输出,可以发现父context的子协程和子context的子协程都会被结束掉
  11. time.Sleep(10 * time.Second)
  12. fmt.Println("最终结束,time=", time.Now().Unix())
  13. }
  14. // 父context的协程
  15. func watch1(ctx context.Context) {
  16. for {
  17. select {
  18. case <-ctx.Done(): //取出值即说明是结束信号
  19. fmt.Println("收到信号,父context的协程退出,time=", time.Now().Unix())
  20. fmt.Println(ctx.Err().Error())
  21. return
  22. default:
  23. fmt.Println("父context的协程监控中,time=", time.Now().Unix())
  24. fmt.Println("获取值", ctx.Value("keys"))
  25. time.Sleep(1 * time.Second)
  26. }
  27. }
  28. }
  29. // 子context的协程
  30. func watch2(ctx context.Context) {
  31. for {
  32. select {
  33. case <-ctx.Done(): //取出值即说明是结束信号
  34. fmt.Println("收到信号,子context的协程退出,time=", time.Now().Unix())
  35. return
  36. default:
  37. fmt.Println("子context的协程监控中,time=", time.Now().Unix())
  38. fmt.Println("子获取值", ctx.Value("keys"))
  39. time.Sleep(1 * time.Second)
  40. }
  41. }
  42. }

锁的使用

互斥锁

错误示范

  1. 使用两个携程进行对一个变量进行操作,理论上讲最终a应该时0
  1. var (
  2. a = 0
  3. wg sync.WaitGroup
  4. )
  5. func main() {
  6. wg.Add(2)
  7. go Add()
  8. go Sub()
  9. wg.Wait()
  10. fmt.Println(a) // 答案不唯一
  11. }
  12. func Add() {
  13. defer wg.Done()
  14. for i := 0; i < 100000; i++ {
  15. a += i
  16. }
  17. }
  18. func Sub() {
  19. defer wg.Done()
  20. for i := 0; i < 100000; i++ {
  21. a -= i
  22. }
  23. }

使用互斥锁实现

  1. var (
  2. a = 0
  3. wg sync.WaitGroup
  4. mutex sync.Mutex
  5. )
  6. func main() {
  7. wg.Add(2)
  8. go Add()
  9. go Sub()
  10. wg.Wait()
  11. fmt.Println(a)
  12. }
  13. func Add() {
  14. defer wg.Done()
  15. for i := 0; i < 100000; i++ {
  16. mutex.Lock()
  17. a += i
  18. mutex.Unlock()
  19. }
  20. }
  21. func Sub() {
  22. defer wg.Done()
  23. for i := 0; i < 100000; i++ {
  24. mutex.Lock()
  25. a -= i
  26. mutex.Unlock()
  27. }
  28. }

控制逻辑处理器数量

  1. var (
  2. a = 0
  3. wg sync.WaitGroup
  4. )
  5. func main() {
  6. // 分配一个逻辑处理器给调度器使用
  7. runtime.GOMAXPROCS(1)
  8. wg.Add(2)
  9. go Add()
  10. go Sub()
  11. wg.Wait()
  12. fmt.Println(a)
  13. }
  14. func Add() {
  15. defer wg.Done()
  16. for i := 0; i < 100000; i++ {
  17. a += i
  18. }
  19. }
  20. func Sub() {
  21. defer wg.Done()
  22. for i := 0; i < 100000; i++ {
  23. a -= i
  24. }
  25. }

Channel 通道

数据获取

  1. func main() {
  2. // 创建无缓冲的通道
  3. chans := make(chan int, 10)
  4. // 方案一 阻塞获取数据
  5. //go func() {
  6. // // 阻塞获取
  7. // data := <-chans
  8. // fmt.Println("获取到数据", data)
  9. //}()
  10. // 方案二 select + for + default 轮询获取
  11. //go func() {
  12. // for {
  13. // select {
  14. // case data := <-chans:
  15. // fmt.Println("获取到数据", data)
  16. // return
  17. // default:
  18. // fmt.Print("等待中")
  19. // }
  20. // }
  21. //}()
  22. // 方案三 select 无 default
  23. // 如果使用 select 加default 则无法获取到数据 需要添加无限循环来处理
  24. //go func() {
  25. // select {
  26. // case data := <-chans:
  27. // fmt.Println("获取到数据", data)
  28. // return
  29. // }
  30. //}()
  31. // 方案四 select + default + for
  32. // 由于 select 存在default分支 故select会结束 但是存在For 所以
  33. // 可以继续执行获取操作
  34. go func() {
  35. for {
  36. select {
  37. case data := <-chans:
  38. fmt.Println("获取到数据", data)
  39. return
  40. default:
  41. fmt.Print("持续等待中...")
  42. }
  43. }
  44. }()
  45. time.Sleep(time.Second * 2)
  46. chans <- a
  47. time.Sleep(time.Second * 1)
  48. }

发送接收案例

  1. // MsgPop 消息获取
  2. func MsgPop(msg chan []byte) {
  3. for v := range msg {
  4. // fmt.Println(v, "获取数据")
  5. SendMsg(v)
  6. fmt.Println("等待中")
  7. }
  8. }
  9. // 消息发送
  10. func MsgPush(cc chan []byte) {
  11. a := map[int]string{
  12. 0: "wx-notice1",
  13. 1: "wm-notice2",
  14. 2: "wm-notice3",
  15. 3: "wm-notice4",
  16. 4: "wm-notice5",
  17. 5: "wm-notice6",
  18. 6: "wm-notice7",
  19. 7: "wm-notice8",
  20. 8: "wm-notice9",
  21. 9: "wm-notice10",
  22. }
  23. for i := 0; i < 10; i++ {
  24. time.Sleep(time.Second)
  25. msg := NoticeType{
  26. MsgType: a[i],
  27. MsgInfo: "zhangsan",
  28. }
  29. data, _ := json.Marshal(msg)
  30. cc <- data
  31. }
  32. }
  33. // 业务实现
  34. func SendMsg(msg []byte) {
  35. var not NoticeType
  36. json.Unmarshal(msg, &not)
  37. fmt.Println("获取到数据", not)
  38. switch not.MsgType {
  39. case "wx-notice1":
  40. fmt.Println("wx-notice1")
  41. case "wm-notice2":
  42. fmt.Println("wm-notice2")
  43. default:
  44. fmt.Println("其他")
  45. }
  46. func main() {
  47. var wg sync.WaitGroup
  48. wg.Add(2)
  49. chanData := make(chan []byte, 10000)
  50. go redis.MsgPop(chanData)
  51. go redis.MsgPush(chanData)
  52. wg.Wait()
  53. }