• 什么时候用Goroutine?什么时候用Channel?


    作者:王中阳
    来源:公众号「程序员升级打怪之旅」
    转载请联系授权(微信ID:wangzhongyang1993)
    原文地址:https://juejin.cn/post/6943952470993272845

    什么场景下用channel合适呢?

    1. 通过全局变量加锁同步来实现通讯,并不利于多个协程对全局变量的读写操作。
    2. 加锁虽然可以解决goroutine对全局变量的抢占资源问题,但是影响性能,违背了原则。
    3. 总结:为了解决上述的问题,我们可以引入channel,使用channel进行协程goroutine间的通信。

    Go语言中的操作系统线程和goroutine的关系:

    1. 一个操作系统线程对应用户态多个goroutine。
    2. go程序可以同时使用多个操作系统线程。
    3. goroutine和OS线程是多对多的关系,即m:n。

    Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信,引出了channel。

    通道channel使用示例:

    for range 从通道中取值,通道关闭时for range 退出

    // channel练习 go  for range从chan中取值
       ch1 := make(chan int)
       ch2 := make(chan int)
    
       // 开启goroutine 把0-100写入到ch1通道中
       go func() {
          for i := 0; i < 100; i++ {
             ch1 <- i
          }
          close(ch1)
       }()
    
    // 开启goroutine 从ch1中取值,值的平方赋值给 ch2
       go func() {
          for {
             i,ok := <-ch1 //通道取值后 再取值 ok = false
             if ok {
                ch2 <- i*i
             }else {
                break
             }
          }
          close(ch2)
       }()
    
    // 主goroutine 从ch2中取值 打印输出
    // for x := chan 有值取值,通道关闭时跳出goroutine
       for i :=range ch2{
          fmt.Println(i)
       }
    

    channel升级,单通道,只读通道和只写通道

    func counter(in chan<- int) {
       defer close(in)
       for i := 0; i < 100; i++ {
          in <- i
       }
    }
    
    func square(in chan<- int, out <-chan int) {
       defer close(in)
       for i := range out {
          in <- i * i
       }
    }
    
    func output(out <-chan int)  {
       for i:=range out{
          fmt.Println(i)
       }
    }
    
    // 改写成单向通道
    func main() {
       ch1 := make(chan int)
       ch2 := make(chan int)
       go counter(ch1)
       go square(ch2, ch1)
       output(ch2)
    }
    

    goroutine work pool,可以防止goroutine暴涨或者泄露

    //使用work pool 防止goroutine的泄露和暴涨
    func worker(id int, jobs <-chan int, results chan<- int) {
       for j := range jobs {
          fmt.Printf("worker:%d start job:%d\n", id, j)
          time.Sleep(time.Second)
          fmt.Printf("worker:%d end job:%d\n", id, j)
          results <- j * 2
       }
    }
          
    func main() {
       jobs := make(chan int, 100)
       results := make(chan int, 100)
       // 开启3个goroutine
       for w := 1; w <= 3; w++ {
          go worker(w, jobs, results)
       }
       // 5个任务
       for j := 1; j <= 5; j++ {
          jobs <- j
       }
       close(jobs)
       // 输出结果
       for a := 1; a <= 5; a++ {
          <-results
       }
    }
    

    goroutine使用select case多路复用,满足我们同时从多个通道接收值的需求

    //使用select语句能提高代码的可读性。
    //可处理一个或多个channel的发送/接收操作。
    //如果多个case同时满足,select会随机选择一个。
    //对于没有case的select{}会一直等待,可用于阻塞main函数。
    ch := make(chan int, 1)
    go func() {
       for i := 0; i < 10; i++ {
          select {
          case x := <-ch:
             fmt.Println(x)
          case ch <- i:
          }
       }
    }()
    

    goroutine加锁 排它锁 读写锁

    var x int64
    var wg sync.WaitGroup
    //添加互斥锁
    var lock sync.Mutex
    
    func main() {
       wg.Add(2)
       go add()
       go add()
       wg.Wait()
       fmt.Println(x)
    }
    
    func add() {
       for i := 0; i < 5000; i++ {
          lock.Lock() //加锁
          x = x + 1
          lock.Unlock() //解锁
       }
       wg.Done()
    }
    

    作者:王中阳
    来源:公众号「程序员升级打怪之旅」
    转载请联系授权(微信ID:wangzhongyang1993)
    原文地址:https://juejin.cn/post/6943952470993272845

  • 相关阅读:
    头部咨询管理企业的数字化转型之路
    06、HSMS协议介绍
    [英语学习] 理解实例:晶振;时钟
    含文档+PPT+源码等]精品微信小程序音乐播放器+后台管理系统|前后分离VUE[包运行成功]计算机毕业设计项目源码Java毕设项目
    功率信号源在实验室中的应用有哪些
    打印数组的所有子集
    opencv形态学-膨胀
    Android Jetpack之ViewModel的使用及源码分析
    健身用什么耳机比较好,盘点五款适合健身场景的耳机
    v-if与v-show
  • 原文地址:https://blog.csdn.net/w425772719/article/details/127042719