47

细说 goroutine 和 channel

 4 years ago
source link: https://studygolang.com/articles/29891
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • goroutine 看一个需求

    • 需求:要求统计 1 - 90000000000 的数字中,那些是素数?
  • 分析思路:

    • 传统的方法,就是使用一个循环,循环的判断各个数是不是素数「效率很低」
    • 使用并发或者并行的方式,将统计素数的任务分配个多个goroutine去完成,这时就会使用到goroutine「速度提高很多」
    • goroutine 基本介绍

    进程和线程介绍

    • 进程就是程序程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单元
    • 线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单元
    • 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行
    • 一个程序至少有一个进程,一个进程至少有一个线程
    • 并发和并行

      • 多线程程序在单核上运行,就是并发
      • 多线程程序在多核上运行,就是并行
    • 小结

      • 并发: 因为是在一个cpu上, 比如有10个线程,每个线程执行10ms(进行轮询操作),从人的角度看,好像这10个线程都在运行,但是从微观上看,

在某个时间点看,其实只有一个线程在执行,这就是并发。

  • 并行: 因为是在多个cpu上(比如有10个cpu),比如有10个线程,每个线程执行10ms(各自在不同的cpu上执行),从人的角度看,这10个线程都在运行,但是从微观上看,在某个时间点看,也同时有10个线程在执行,这就是并行
  • go 协程和go主线程

    • go主线程(有程序员直接称为线程/也可以理解程进程) : 一个go线程上,可以起多个协程,你可以这里理解,协程是轻量级的线程「编译器做优化」
  • go 协程的特点

    • 有独立的栈空间
    • 共享程序堆空间
    • 调度由用户控制
    • 协程是轻量级的线程
  • channel (管道)-看个需求

    • 需求: 现在要计算1-200 的各个数的阶乘,并且把各个数的阶乘放入到map中,最后显示出来,要求使用goroutine完成

分析思路

  • 使用goroutine来完成,效率高,但是会出现并发/并行安全问题
  • 这里就提出了不同的goroutine如何通信的问题

代码实现

  • 使用goroutine 来完成(看看使用goroutine并发完成会出现什么问题? 然后我们去解决)
  • 在运行某个程序时,如何知道是否存在资源竞争问题,方法很简单,在编译程序时。增加一个参数。 -race即可
  • 代码实现见 goroutine 和 channel/goroutine_channel.go

    上面的案例就出现了资源的竞争的问题

  • 不同goroutine之间如何通讯

    • 全局变量的互斥锁
    • 使用管道channel来解决
  • 使用全局变量加锁同步改进程序

    • 因为没有对全局变量m加锁,因此会出现资源争夺问题,代码会出现错误,提示 fatal error: concurrent map writes
    • 解决方案: 加入互斥锁
    • 我们的数的阶乘很大,结果会越界,可以将求阶乘改成sum+=uint64(i)
    • 改进代码实现见 goroutine 和 channel/goroutine_channel_new.go
  • 为什么需要channel

    • 前面使用全局变量加锁同步来解决goroutine 的通讯,但不完美
    • 主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10s,仅仅是估算
    • 如果主线程休眠时间长了,会加长等待时间,如果时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁
    • 同步全局变量加锁同步来通讯,也不利用多个协程对全局变量的读写操作
    • 上面种种分析都在呼唤一个新的通讯机制- channel
  • channel 的基本介绍

    • channel本质就是一个数据结构-队列
    • 数据是先进先出(FIFO: first in first out)
    • 线程安全,多goroutine访问时,不需要加锁,就是说channel本身就是线程安全的
    • channel有类型的,一个string的channel只能存放string类型数据
    • channel 是线程安全的,多个协程操作同一个管道时,不会发生资源竞争问题
  • 定义/声明channel

    • var 变量名 chan 数据类型
    • 举例:

      var intChan chan int (intChan 用户存放int数据)
      var mapChan chan map[int]string (mapChan 用户存放map[int]string类型)
      var perChan chan Person
      var PerChan2 chan *Person
      ....

      说明

      • channel 是引用类型
      • channel必须初始化才能写入数据,即make后才能使用
      • 管道是有类型的,intChan只能写入整型int
  • 管道的初始化,写入数据到管道,从管道读取数据以及基本注意事项
    • 代码实现见 goroutine 和 channel/channel.go
  • channel 使用的注意事项

    • channel 中只能存放指定的数据类型
    • channel 的数据放满后,就不能再放入了
    • 如果从channel取出数据后,可以继续放入
    • 在没有使用协程的情况下,如果channel数据取完了,在取,就会报dead lock
  • 读写channel 案例演示

    • 案例见 goroutine 和 channel/intChan.go

.....详细见下面连接

细说 goroutine 和 channel

微信扫码关注站长公众号,和站长交流学习

bUJrayM.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK