九:goroutine(1.什么是协程)

goroutine

这是Go最大的特点。

必须掌握:

  • goroutine
  • channel
  • select
  • sync.WaitGroup
  • Mutex
  • RWMutex
  • Once
  • Cond
  • atomic

按照下面顺序系统学习:

yaml 复制代码
1. 并发基础概念
        |
        v
2. goroutine(轻量级线程)
        |
        v
3. channel(通信机制)
        |
        v
4. select(多路复用)
        |
        v
5. sync.WaitGroup(等待任务完成)
        |
        v
6. Mutex / RWMutex(共享内存同步)
        |
        v
7. Once(只执行一次)
        |
        v
8. Cond(条件变量)
        |
        v
9. atomic(无锁原子操作)

一、Go 并发设计思想

先理解一句 Go 的哲学:

不要通过共享内存来通信,而应该通过通信来共享内存。

英文:

vbnet 复制代码
Don't communicate by sharing memory;
share memory by communicating.

这句话非常重要。

java传统模型

多个线程访问同一块内存:

count++;

需要:

arduino 复制代码
synchronized
Lock
volatile
AtomicInteger

这叫:

数据竞争(Data Race)

Go 更推荐:

css 复制代码
goroutine A

      |
      |
   channel
      |
      |
goroutine B

数据通过 channel 流动。

二、goroutine

Java Thread

操作系统线程:

markdown 复制代码
Java Thread

      |
      |
 OS Thread

      |
      |
 CPU Core

线程由 OS 调度。

创建成本:

makefile 复制代码
线程栈:
1MB左右

10万个线程:

复制代码
100GB内存

几乎不可行。

Go goroutine:这部分详细请看本专栏的GMP详解

结构:

markdown 复制代码
goroutine

    |
    |
 Go Runtime Scheduler

    |
    |
 OS Thread

    |
    |
 CPU

Go 自己实现调度。

叫:

GMP 调度模型

三、创建 goroutine

最简单:

go 复制代码
package main

import (
    "fmt"
    "time"
)

func hello(){

    fmt.Println("hello")

}


func main(){

    go hello()

    time.Sleep(time.Second)

}

关键:

scss 复制代码
go hello()

表示:

创建一个新的 goroutine。


执行流程:

main goroutine:

yaml 复制代码
main

 |
 |
 创建

 |
 v

hello goroutine

两个并发执行。

四、goroutine 生命周期

非常重要。

例如:

go 复制代码
func main(){

    go func(){

        fmt.Println("hello")

    }()

}

运行:

css 复制代码
main结束

↓

整个程序退出

↓

所有goroutine结束

所以:

输出:

可能:

复制代码
什么都没有

因为:

goroutine还没有运行。

所以要有主次之分,生产环境中,我们常用WaitGroup,让主协程来管理所有的goroutine

五、goroutine 调度

Go不是:

复制代码
一个goroutine对应一个线程

而是:

markdown 复制代码
100万个goroutine

        |
        |
      scheduler

        |
        |
    几个线程执行

例如:

服务器:

复制代码
10000请求

↓

10000 goroutine

↓

几十个线程

所以 Go 非常适合:

  • 网络服务
  • RPC
  • 微服务
  • IO密集任务

六、goroutine 常见问题

1. goroutine泄漏

例如:

go 复制代码
func worker(ch chan int){

    for{

        data:=<-ch

        fmt.Println(data)

    }

}

如果:

没人发送:

复制代码
worker永久阻塞

goroutine泄漏。


生产环境:

必须考虑:

复制代码
退出机制

通常:

context。

2. 闭包变量捕获问题

经典:

错误:

css 复制代码
for i:=0;i<10;i++{

    go func(){

        fmt.Println(i)

    }()

}

输出:

可能:

erlang 复制代码
10
10
10
...

原因:

所有goroutine共享:

css 复制代码
同一个i地址

正确:

css 复制代码
for i:=0;i<10;i++{

    i:=i

    go func(){

        fmt.Println(i)

    }()

}

或者:

css 复制代码
for i:=0;i<10;i++{

    go func(n int){

        fmt.Println(n)

    }(i)

}

7、面试重点

问题一:为什么 goroutine 比线程轻?

回答:

goroutine 由 Go runtime 管理,采用 GMP 的 M:N 调度模型,不需要一个 goroutine 对应一个 OS Thread;同时 goroutine 初始栈很小,而且栈可以动态扩缩容。因此相比传统 OS Thread,goroutine 的创建和调度成本更低,可以支持大量并发 goroutine。

问题二:goroutine 什么时候切换?

回答:

goroutine 在阻塞或者被调度器抢占时会发生调度切换。常见情况包括系统调用、channel 阻塞、网络 IO、定时器等待,以及 Go runtime 的抢占式调度。

相关推荐
不是万能的豆学长1 小时前
排查Go服务40天后的内存泄漏问题
开发语言·后端·golang·go
阿狗童鞋1 小时前
SpringSecurity权限认证实战指南
java·后端·spring
韩振方1 小时前
Kubernetes Service 让会更换的 Pod 有稳定访问入口
后端
用户125758524361 小时前
Golang后台管理框架推荐:XYGo Admin与devinggo怎么选?
后端·go·ai编程
花间相见1 小时前
【计算基础|网络13】UDP 与可靠传输
后端·面试
tachibana21 小时前
Go 语言 Map 的底层实现原理
开发语言·后端·golang·make
九零HTTP1 小时前
从 Cookie 到 Token,登录态到底是怎么维持的
后端
Gopher_HBo2 小时前
go-grpc服务端调用链
后端
程序边界2 小时前
用了三年数据同步软件,我想好好聊聊KFS这套东西到底强在哪(上)
后端