Golang goroutine、sync.WaitGroup、Channel实现并发详解

goroutine 概念

goroutine是建立在线程之上的轻量级的抽象,它允许我们以非常低的代价在同一个地址空间中并行执行多个函数或者方法。相比于线程它的创建和销毁的代价要小很多,并且它的调用是独立于线程的,在golang中创建一个goroutine非常简单,使用“go” 关键字即可

 

Gorouine 与线程的区别

  1. 内存消耗更低: goroutine初始化只有2kb 而线程需要1MB
  2. 创建与销毁的开销更小: goroutine的创建和销毁是由go语言在运行时自己管理的,不需要在操作系统申请创建线程的资源
  3. goroutine是用户态的线程 ,比内核态的线程更加轻量

goroutine的调度

goroutine的调度方式是协同式的,在协同式调度中,没有时间片的概念,为了并行执行goroutine调度器会在一下时间点进行切换

  1. Channel接受或者发送会造成阻塞的消息
  2. 当一个新的goroutine被创建时
  3. 可以造成阻塞的系统调用,如文件和网络操作
  4. 垃圾回收

goroutine 调度器的概念

Golang的调度器可以利用多个processor资源在任意时刻,M个goroutine需要调度到N个OSThread上同时这些thread运行在多个GOMAXPROCS个processor

  • processor(P) 是一个本地goroutine队列,同时有一个全局的队列
  • OSThread(M) 都会分配一个processor 最多只能有gomaxprocs 个数
  • Goroutine(G) 多个Goroutine 调度到OSThread上

 runtime.GOMAXPROCS

  1. runtime.GOMAXPROCS(1)  只占用1核cpu
package main

import (
    "fmt"
    "runtime"
    "time"
)

func SleepGroutine() {
    fmt.Println("start")
     time.Sleep(time.Microsecond * 5)
     fmt.Println("stop")
}
func main(){
    fmt.Println("processor start .............")
   
    runtime.GOMAXPROCS(10)
    go SleepGroutine()
    time.Sleep(time.Second *5)
    fmt.Println("processor stop .............")
}

 

sync.WaitGroup


package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)
 
// 使用WaitGroup方式等待执行完成之后优雅的退出
var wg sync.WaitGroup

func WaitGroupGoroutine(nums int){
    time.Sleep(time.Millisecond * 500)
    fmt.Printf("use waitGroup test goroutine,%v\n",nums)
    //计数器-1
    wg.Done()
}

func main(){
    fmt.Println("processor start .............")
   
    runtime.GOMAXPROCS(10)
   
    for i :=0 ; i<20;i++{
        // 计数器+1
        wg.Add(1)
        go WaitGroupGoroutine(i)
    }
    // 等待计数器 0 时推出等待组
    wg.Wait()

    fmt.Println("processor stop .............")
}
 

 

  

channel 通道

  1. 通过channel实现多个goroutine 之间的通信
  2. channel:是一种类型 一种引用的类型 make 函数初始化之后才能使用(slice,map,channel)
  3. channel的声明: var ch chan
  4. channel初始化: b = make(chan 元素 [缓冲区大小]) 
  5. channel 的操作
    1. 发送:   ch1 <- 100 
    2.  接收:x := <- ch1
    3.   关闭:close(ch1)
  6. 待缓冲区的通道和不带缓冲区的通道
    1. a = make(chan int )         不带缓冲区的通道   
    2. b = make(chan int ,10 )   待缓冲区的通道 
  7. 单项通道,通常作为函数的参数
    1. 只读通道: <- chan int
    2. 只写通道:chan<- int

 

package selectdemo_test
//select 学习
import (
    "fmt"
    "testing"
    "time"
)
// 声明一个函数,模拟服务
func service() string{
    time.Sleep(time.Millisecond * 500)
    return "Done"
}
func AsyncService() chan string{
    retCh := make(chan string ,1)
    // 开启一个goroutine 
    go func(){
        ret :=service()
        fmt.Println("returned result...")
        // 把service执行的结果放进channel里面
        retCh <- ret
        fmt.Println("service exited...")
        
    }()
    return retCh
}
func TestSelect(t *testing.T){
    //多路选择使用select方式
    select{
    //阻塞事件等待chan 等待通道发来的消息
    case ret := <-AsyncService():
        t.Log(ret)
    // 超时控制当时间没有到定义的时间时会阻塞这个case直到超时。
    case <- time.After(time.Millisecond * 100):
        t.Error("time out")
    }
}

 

posted @ 2023-10-15 16:04  扛把子修BUG  阅读(19)  评论(0编辑  收藏  举报