您的位置:首页 > 编程语言 > Go语言

Golang:线程 和 协程 的区别

2019-10-07 11:28 1476 查看

作者:林冠宏 / 指尖下的幽灵

博客:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities

目录

  • 前言
  • 协程
  • 协程的特点
    第 1
    第 2
  • 特点中的第 3 和 第 4 点
  • 和线程的整体对比
  • 前言

    国庆越快各位,距离上次发文快两个月了,19年也快结束了。现在的总结更多是放在了

    草稿
    而没有发出,这次详细分享下在 Go 中,
    线程和协程的区别及其关系

    协程

    协程,英文名

    Coroutine
    。但在 Go 语言中,协程的英文名是:
    gorutine
    。它常常被用于进行
    多任务
    ,即
    并发作业
    。没错,就是
    多线程
    作业的那个作业。

    虽然在 Go 中,我们不用直接编写线程之类的代码来进行并发,但是 Go 的协程却

    依赖于线程
    来进行。

    下面我们来看看它们的区别。

    线程的基础介绍,这里请自行网上搜索文章,因为关于线程的优秀介绍文章已经很多。

    协程的特点

    这里先直接列出线程的特点,然后从例子中进行解析。

    1. 多个协程可由一个或多个线程管理,
      协程的调度
      发生在其所在的线程中。
    2. 可以被调度,调度策略由应用层代码定义,即可被高度自定义实现。
    3. 执行效率高。
    4. 占用内存少。

    上面
    第 1
    第 2

    我们来看一个例子:

    func TestGorutine(t *testing.T) {
    runtime.GOMAXPROCS(1)  // 指定最大 P 为 1,从而管理协程最多的线程为 1 个
    wg := sync.WaitGroup{} // 控制等待所有协程都执行完再退出程序
    wg.Add(2)
    // 运行一个协程
    go func() {
    fmt.Println(1)
    fmt.Println(2)
    fmt.Println(3)
    wg.Done()
    }()
    
    // 运行第二个协程
    go func() {
    fmt.Println(65)
    fmt.Println(66)
    // 设置个睡眠,让该协程执行超时而被挂起,引起超时调度
    time.Sleep(time.Second)
    fmt.Println(67)
    wg.Done()
    }()
    wg.Wait()
    }

    上面的代码片段跑了两个协程,运行后,观察输出的

    顺序是交错
    的。可能是:

    65
    66
    1
    2
    3
    67

    意味着在执行协程A的过程中,可以

    随时中断
    ,去执协程行B,协程B也可能在执行过程中中断再去执行协程A。

    看起来协程A 和 协程B 的运行像是线程的切换,但是请注意,这里的 A 和 B 都运行在同一个线程里面。它们的调度不是线程的切换,而是

    纯应用态的协程调度

    关于上述代码中,为什么要指定下面两行代码?

    runtime.GOMAXPROCS(1)
    time.Sleep(time.Second)

    这需要您去看下 Go 的协程调度入门基础,请看我之前的另外一篇调度分析文章:

    Go 的协程调度机制

    如果不设置

    runtime.GOMAXPROCS(1)
    ,那么程序将会根据操作系统的 CPU 核数而启动对应数量的 P,导致多个 M,即线程的启动。那么我们程序中的协程,就会被
    分配到不同的线程
    里面去了。为了演示,故设置数量 1,使得它们都被分配到了同一个线程里面,存于线程的协程队列里面,等待被执行或调度。

    协程特点中的第 3 和 第 4 点。

    1. 执行效率高。
    2. 占用内存少。

    因为

    协程的调度切换不是线程切换
    ,而是由程序自身控制,因此,
    没有线程切换的开销
    ,和多线程比,线程数量越多,协程的性能优势就越明显。调度发生在应用态而非内核态。

    内存的花销,使用其所在的线程的内存,意味着线程的内存可以供多个协程使用。

    其次协程的调度

    不需要多线程的锁机制
    ,因为只有一个线程,也
    不存在同时写变量冲突
    ,所以执行效率比多线程高很多。

    和线程的整体对比

    比较的点 线程 协程
    数据存储 内核态的内存空间 一般是线程提供的用户态内存空间
    切换操作 操作最终在内核层完成,应用层需要调用内核层提供的 syscall 底层函数 应用层使用代码进行简单的现场保存和恢复即可
    任务调度 由内核实现,抢占方式,依赖各种锁 由用户态的实现的具体调度器进行。例如 go 协程的调度器
    语音支持程度 绝大部分编程语言 部分语言:Lua,Go,Python ...
    实现规范 按照现代操作系统规范实现 无统一规范。在应用层由开发者实现,高度自定义,比如只支持单线程的线程。不同的调度策略,等等
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: