• 技术文章 >后端开发 >Golang

    Go语言中goroutine是啥

    青灯夜游青灯夜游2023-01-11 15:33:57原创68

    goroutine是Go语言中的轻量级线程实现,是建立在线程之上的轻量级的抽象,由Go运行时(runtime)管理。goroutine允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法;相比于线程,它的创建和销毁的代价要小很多,并且它的调度是独立于线程的。

    本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。

    在编写 Socket 网络程序时,需要提前准备一个线程池为每一个 Socket 的收发包分配一个线程。开发人员需要在线程数量和 CPU 数量间建立一个对应关系,以保证每个任务能及时地被分配到 CPU 上进行处理,同时避免多个任务频繁地在线程间切换执行而损失效率。

    虽然,线程池为逻辑编写者提供了线程分配的抽象机制。但是,如果面对随时随地可能发生的并发和线程处理需求,线程池就不是非常直观和方便了。能否有一种机制:使用者分配足够多的任务,系统能自动帮助使用者把任务分配到 CPU 上,让这些任务尽量并发运作。这种机制在 Go语言中被称为 goroutine。

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。

    Goroutine是建立在线程之上的轻量级的抽象。它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法。相比于线程,它的创建和销毁的代价要小很多,并且它的调度是独立于线程的。

    Go 程序从 main 包的 main() 函数开始,在程序启动时,Go 程序就会为 main() 函数创建一个默认的 goroutine。

    使用普通函数创建 goroutine

    Go 程序中使用 go 关键字为一个函数创建一个 goroutine。一个函数可以被创建多个 goroutine,一个 goroutine 必定对应一个函数。

    1) 格式

    为一个普通函数创建 goroutine 的写法如下:

    go 函数名( 参数列表 )

    使用 go 关键字创建 goroutine 时,被调用函数的返回值会被忽略。

    如果需要在 goroutine 中返回数据,请使用后面介绍的通道(channel)特性,通过通道把数据从 goroutine 中作为返回值传出。

    2) 例子

    使用 go 关键字,将 running() 函数并发执行,每隔一秒打印一次计数器,而 main 的 goroutine 则等待用户输入,两个行为可以同时进行。请参考下面代码:

    package main
    import (
        "fmt"
        "time"
    )
    func running() {
        var times int
        // 构建一个无限循环
        for {
            times++
            fmt.Println("tick", times)
            // 延时1秒
            time.Sleep(time.Second)
        }
    }
    func main() {
        // 并发执行程序
        go running()
        // 接受命令行输入, 不做任何事情
        var input string
        fmt.Scanln(&input)
    }

    命令行输出如下:

    1.png

    代码执行后,命令行会不断地输出 tick,同时可以使用 fmt.Scanln() 接受用户输入。两个环节可以同时进行。

    代码说明如下:

    这段代码的执行顺序如下图所示。

    2.jpg
    图:并发运行图

    这个例子中,Go 程序在启动时,运行时(runtime)会默认为 main() 函数创建一个 goroutine。在 main() 函数的 goroutine 中执行到 go running 语句时,归属于 running() 函数的 goroutine 被创建,running() 函数开始在自己的 goroutine 中执行。此时,main() 继续执行,两个 goroutine 通过 Go 程序的调度机制同时运作。

    使用匿名函数创建goroutine

    go 关键字后也可以为匿名函数或闭包启动 goroutine。

    1) 使用匿名函数创建goroutine的格式

    使用匿名函数或闭包创建 goroutine 时,除了将函数定义部分写在 go 的后面之外,还需要加上匿名函数的调用参数,格式如下:

    go func( 参数列表 ){
        函数体
    }( 调用参数列表 )

    其中:

    2) 使用匿名函数创建goroutine的例子

    在 main() 函数中创建一个匿名函数并为匿名函数启动 goroutine。匿名函数没有参数。代码将并行执行定时打印计数的效果。参见下面的代码:

    package main
    import (
        "fmt"
        "time"
    )
    func main() {
        go func() {
            var times int
            for {
                times++
                fmt.Println("tick", times)
                time.Sleep(time.Second)
            }
        }()
        var input string
        fmt.Scanln(&input)
    }

    代码说明如下:

    扩展知识:Goroutine与线程的区别

    许多人认为goroutine比线程运行得更快,这是一个误解。Goroutine并不会更快,它只是增加了更多的并发性。当一个goroutine被阻塞(比如等待IO),golang的scheduler会调度其他可以执行的goroutine运行。与线程相比,它有以下的几个优点:

    内存消耗更少:

    Goroutine所需要的内存通常只有2kb,而线程则需要1Mb(500倍)

    创建与销毁的开销更小:

    由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。

    切换开销更小:

    只是goroutine之于线程的主要区别,也是golang能够实现高并发的主要原因。线程的调度方式是抢占式的,如果一个线程的执行时间超过了分配给它的时间片,就会被其他可执行的线程抢占。在线程切换的过程中需要保存/恢复所有的寄存器信息,比如16个通用寄存器,PC(Program Counter)、SP(Stack Pointer)段寄存器等等。而goroutine的调度是协同式的,它不会直接地与操作系统内核打交道。当goroutine进行切换的时候,之后很少量的寄存器需要保存和恢复(PC和SP)。因此goroutine的切换效率更高。

    【相关推荐:Go视频教程编程教学

    以上就是Go语言中goroutine是啥的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:goroutine go语言 Golang
    上一篇:go语言接口类型怎么转换 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • Go语言变量的生命周期是啥• go语言怎么替换字符串• goroutine和coroutine的区别是什么• Go语言中chan通道是什么• Go语言常用内置包有哪些
    1/1

    PHP中文网