golang并发和并发模式

本文介绍了Go语言中的并发和并行概念,重点讲解了goroutine、waitgroup、资源锁(原子操作和互斥锁)、通道的使用,以及如何避免竞争状态。还探讨了无缓冲和有缓冲通道的区别,并提出了使用通道控制程序生命周期和实现goroutine池的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 并发和并行

    • golang里的并发指的是让某个函数独立于其他函数执行的能力(goroutine运行时是相互独立的)。

    • 并行是让多个不同的代码片段同时在不同的物理处理器上执行。

    • 并行是同时做很多事情,并发是同时管理很多事情。

    • goroutine
      • waitgroup
        • waitgroup.add(int)  //int为需要运行的goroutine数量
        • waitgroup.done()  //标识一个goroutine运行结束
        • waitgroup.wait()  //等待所有goroutine运行结束
      • 多个goroutine同时并发运行时,如果当前执行的goroutine占用逻辑处理器时间过长,调度器会暂停当前执行的goroutine并切换到其他goroutine执行,其他goroutine执行结束或执行其他goroutine一定时间之后,调度器会切换回暂停的goroutine继续执行。
    • Runtime.GOMAXPROCS(1)/runtime.GOMAXPROCS(runtime.NumCPU)
      • GOMAXPROCS()函数可以为可用的物理处理器(CPU)创建逻辑处理器
    • Runtime.Gosched()
      • 当前goroutine从线程退出,并放回到队列
    • 竞争状态
      • 如果两个或多个goroutine在没有相互同步的情况下,访问某个共享资源,并试图同时读和写这个资源,就处于相互竞争的状态。
      • 对一个共享资源的读和写操作必须是原子化的。
    • 资源锁
      • 传统同步goroutine机制,就是对共享资源加锁。
      • 原子函数
        • 原子函数能以很底层的加锁机制来同步访问整型变量和指针。
        • atomic.AddInt64(&num,n)为int64类型整型数值安全的增加n(串行增加)
        • atomic.LoadInt64(&num) 取出int64类型整型的值
        • atomic.StoreInt64(&num,n)为int64类型整型数值赋值为n
      • 互斥锁
        • 互斥锁用于在代码上创建一个临界区,保证同一时间只有一个goroutine能够访问临界区的代码
        • sync.Mutex----->mutex.Lock(){代码块}----->mutex.Unlock()
        • 当一个goroutine进入临界区,并在临界区代码块执行过程中强制退出当前线程(runtime.Gosched()),调度器会字词分配这个goroutine进入线程继续运行,直到该goroutine执行Unlock()释放代码块,其他goroutine才能进入该临界区
    • 通道
      • 除了原子函数和互斥锁外,通道也能保证对共享资源的安全访问以及消除竞争状态,通道通过发送和接收需要共享的资源,在goroutine之间做到资源同步。
      • 声明通道时,需要指定将要被共享的数据的类型
      • 可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针
      • 通道需要通过内置函数make()来创建通道。
        • chan := make(chan int)----------无缓冲通道
        • buffer := make(chan string,10)---------有缓冲通道
        •  创建通道需要使make函数的第一个参数是关键字“chan”,之后跟着允许通道交换的数据类型,如果创建的是有缓冲通道,还需要第二个参数指定该通道的缓冲区大小
        • 通过通道发送值或指针需要用到“<-”操作符
          • buffer <- "abcd"

        • goroutine从通道获取数据

          • value := <-buffer

      • 无缓冲通道
        • 无缓冲通道是指在接收前没有能力保存任何值的通道,这种通道需要发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。
        • 如果发送goroutine和接收goroutine没有同时准备好,会造成发送或接收操作的goroutine阻塞等待。
      • 有缓冲通道
        • 有缓冲通道是一种在被接收前能存储一个或者多个值的通道。不强制要求goroutine之间必须同时完成发送和接受。
        • 通道中没有要接收的值时,接收动作会阻塞。
        • 通道没有可用的缓冲区容纳被发送的值时,发送动作会阻塞。
        • 无缓冲通道能保证发送和接受会在同一时间进行数据交换,有缓冲通道没有这种保证。
      • 总结:
        • 并发是指goroutine之间相互独立的运行
        • goroutine在逻辑处理器上执行,逻辑处理器具有独立的系统线程和运行队列
        • 竞争状态是指两个或多个goroutine试图同时访问同一个资源
        • 原子函数和互斥锁可以防止竞争状态
  • 并发模式

    • 程序可以在分配的时间内完成工作,正常终止
    • 程序没有及时完成工作,“自杀”
    • 接受到操作系统发送的中断事件,程序立刻尝试清理状态并终止工作。
    • 使用通道来监视程序的执行时间,如果程序运行时间太长,可以终止程序
      • 程序没有及时完成工作,“自杀”
    • 使用通道监视操作系统中断事件(os.signal),打断程序运行
    • pool包(sync/pool.go)
      • 用于使用有缓冲通道实现资源池,管理可以在任意数量的goroutine之间共享及独立使用的资源。(共享一组静态资源的情况下,如共享数据库连接或者内存缓冲区)。
      • 实现io.Closer接口的资源就可以用pool资源池来管理。
      • 向pool包提供共享资源的工厂函数和资源数量,由pool包来管理资源池
    • work包
      • 用于使用无缓冲的通道来创建一个goroutine池,这些goroutine执行并控制一组工作,让其并发执行。
      • 因为执行相同的工作,goroutine既不需要工作队列,也不需要相互配合执行
      • 当goroutine池中所有的goroutine都忙,无法接收新的工作时,能及时通过通道通知调用者
      • 使用无缓冲通道不会有工作在队列里丢失或者卡住,所有工作都会被处理。
    • 总结:
      • 可以使用通道来控制程序的声明周期(正常终止,中断,超时(“自杀”)等)
      • select的default语句可以用来尝试向通道发送或接收数据,而不会阻塞
      • 有缓冲的通道适合用来管理可复用的资源
      • 有缓冲通道适合用来处理并发任务
      • 无缓冲通道可以保证两个goroutine之间的数据交换,且保证发送方对方接收到了数据
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值