Go 并发

1. 线程

开始线程只需要使用go关键字即可

2. 线程等待

主线程需要所有子线程执行完成后才结束

线程等待包 sync.WaitGroup
添加等待线程数 waitGroup.Add(100)
子线程结束标志 waitGroup.Done()
主线程等待标志 waitGroup.Wait()

import (
   "fmt"
   "sync"
)

func main() {
   var waitGroup sync.WaitGroup
   // 添加需要等待线程的数量
   waitGroup.Add(100)
   for i := 0; i < 100; i++ {
      // 开启线程
      go func(x int) {
         // defer定义的每次执行完成之前减少线程等待数量
         defer waitGroup.Done()
         fmt.Println(x)
      }(i)
   }
   // 等待子线程结束 在执行后面的逻辑
   waitGroup.Wait()
}

3.线程锁

防止线程中重要逻辑还没完成就切换线程导致的错误

  • sync.Mutex
import (
   "fmt"
   "sync"
)

var waitGroup sync.WaitGroup
var lock sync.Mutex
var total int32

func main() {
   waitGroup.Add(2)
   go add()
   go sub()
   waitGroup.Wait()
   fmt.Println(total)
}
func add() {
   defer waitGroup.Done()
   for i := 0; i < 100000; i++ {
      // 计算前加锁
      lock.Lock()
      total += 1
      // 计算后解锁
      lock.Unlock()
   }
}
func sub() {
   defer waitGroup.Done()
   for i := 0; i < 100000; i++ {
      lock.Lock()
      total -= 1
      lock.Unlock()
   }
}
  • sync.RWMutex

读写锁 var lock sync.RWMutex
lock.Lock() lock.Unlock()写锁会阻塞其他线程的度和写
lock.RLock() lock.RUnlock()读锁不会阻塞其他线程的读

  • atomic原子锁

需要传指针类型

import (
   "fmt"
   "sync"
   "sync/atomic"
)

var waitGroup sync.WaitGroup
var lock sync.Mutex
var total int32

func main() {
   waitGroup.Add(2)
   go add()
   go sub()
   waitGroup.Wait()
   fmt.Println(total)
}
func add() {
   defer waitGroup.Done()
   for i := 0; i < 100000; i++ {
      // 需要注意的是这块的值是指针
      atomic.AddInt32(&total, 1)
   }
}
func sub() {
   defer waitGroup.Done()
   for i := 0; i < 100000; i++ {
      // 需要注意的是这块的值是指针
      atomic.AddInt32(&total, -1)
   }
}

4. channel 线程间通信

  • channel定义
// var 变量名称 chan channel内部类型
var msg chan string

// 空间为0,无缓冲类型,适用于消息通知
msg = make(chan string, 0)
// 空间大于0,有缓冲类型,适用于生产者和消费者之间的通信
msg = make(chan string, 1)
  • channel读写
msg := make(chan string, 1)
// 写数据
msg <- "向channel中写数据"

// 读数据
str := <-msg
  • 循环channel

循环channel用for
关闭channel用close

func main() {
   msg = make(chan string, 1)

   go func(msg chan string) {
      // for 循环一直取msg中的内容,会一直阻塞在这块
      for data := range msg {
         fmt.Println(data)
      }
      fmt.Println("all done")
   }(msg)
   for i := 0; i < 10; i++ {
      time.Sleep(time.Millisecond * 500)
      msg <- strconv.FormatInt(int64(i), 10)
   }
   // 关闭channel,上面的for循环就会退出 
   close(msg)
   time.Sleep(time.Second * 10)
}

示例:字母数字交替打印AB12CD34EF56GH78IJ910KL1112MN1314OP1516QR1718ST1920UV2122WX2324YZ2526

import (
   "fmt"
   "time"
)

var num, word = make(chan bool), make(chan bool)

func printNum() {
   i := 1
   for {
      <-num
      fmt.Printf("%d%d", i, i+1)
      i += 2
      word <- true

   }
}

func printWord() {
   i := 65
   for {
      <-word
      if i > 90 {
         return
      }
      fmt.Printf("%c%c", i, i+1)
      i += 2
      num <- true
   }
}

func main() {
   go printNum()
   go printWord()
   word <- true
   time.Sleep(time.Second * 5)
}
  • 单向channel
// 只能读取
var reader <-chan string
// 只能写入
var writer chan<- string

reader = make(<-chan string)
writer = make(chan<- string)

示例

func main() {
   // channel定义时可以是双向channel
   ch := make(chan string, 0)
    
   // 函数定义时直接定义单向channel
   go func(reader <-chan string) {
      for msg := range reader {
         fmt.Println(msg)
      }
   }(ch)
   // 函数定义时直接定义单向channel
   go func(writer chan<- string) {
      for i := 0; i < 10; i++ {
         time.Sleep(time.Millisecond * 500)
         writer <- "写入中"
      }
      close(ch)
   }(ch)  

   time.Sleep(time.Second * 5)
}
  • select对多个channel的监控
import (
   "fmt"
   "time"
)

func func1(ch chan string) {
   time.Sleep(time.Second)
   ch <- "done"
}

func func2(ch chan string) {
   time.Sleep(2 * time.Second)
   ch <- "done"
}

func main() {
   var ch1, ch2 = make(chan string), make(chan string)
   go func1(ch1)
   go func2(ch2)
    
   // 这块会生成一个channel,5秒之后就会往里添加数据,用来超时的
   t := time.NewTimer(time.Second * 5)
   // 主要代码在这
   for {
      select {
      case <-ch1:
         fmt.Println("ch1 down")
      case <-ch2:
         fmt.Println("ch2 down")
      case <-t.C:
         fmt.Println("超时")
         return
      default:
		time.Sleep(time.Millisecond * 100)
		fmt.Println("其他通道没有值")
      }
   }
   time.Sleep(6)
}
  • context

context是个树型结构上下文,返回的context可以感知到父context退出,最顶层context为context.Background()

    • 主动退出型context
ctx, exit := context.WithCancel(context.Background())
import (
   "context"
   "fmt"
   "time"
)

func func1(ctx context.Context) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println("接收到主函数退出通知")
         return
      default:
         time.Sleep(time.Second)
         fmt.Println("等待主函数发送结束消息")
      }
   }
}

func main() {
   ctx, exit := context.WithCancel(context.Background())
   go func1(ctx)

   time.Sleep(time.Second * 2)
   // 手动调用上面返回的exit,这会给ctx发个信号
   exit()
   time.Sleep(time.Second)
}
    • 超时退出型context
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
import (
   "context"
   "fmt"
   "time"
)

func func1(ctx context.Context) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println("接收到主函数退出通知")
         return
      default:
         time.Sleep(time.Second)
         fmt.Println("等待主函数发送结束消息")
      }
   }
}

func main() {
   // 这里的ctx2秒就会退出
   ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
   go func1(ctx)
   time.Sleep(time.Second * 5)
}
    • 指定时间退出型context
ctx, _ := context.WithDeadline(context.Background(), time.Now())
import (
   "context"
   "fmt"
   "time"
)

func func1(ctx context.Context) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println("接收到主函数退出通知")
         return
      default:
         time.Sleep(time.Second)
         fmt.Println("等待主函数发送结束消息")
      }
   }
}

func main() {
   // 这个会在指定的时间退出 
   ctx, _ := context.WithDeadline(context.Background(), time.Now())
   go func1(ctx)
   time.Sleep(time.Second * 5)
}
    • 传值型context
ctx1, _ := context.WithTimeout(context.Background(), time.Second*2)
ctx2 := context.WithValue(ctx1, "ping", "pong")
import (
   "context"
   "fmt"
   "time"
)

func func1(ctx context.Context) {
   // 这块接收到的值 
   fmt.Println(ctx.Value("ping"))
   for {
      select {
      case <-ctx.Done():
         fmt.Println("接收到主函数退出通知")
         return
      default:
         time.Sleep(time.Second)
         fmt.Println("等待主函数发送结束消息")
      }
   }
}

func main() {
   ctx1, _ := context.WithTimeout(context.Background(), time.Second*2)
   // 这里可以使用上面返回的超时context,在不影响超时退出的情况下,增加了传值 
   ctx2 := context.WithValue(ctx1, "ping", "pong")
   go func1(ctx2)
   time.Sleep(time.Second * 5)
}
posted @ 2023-03-15 10:45  ForLivetoLearn  阅读(22)  评论(0编辑  收藏  举报