Go 快速入门指南 - goroutine

概述

goroutine 是 Go 程序并发执行的实体,对于初学者来讲,可以简单地将 goroutine 理解为一个 超轻量的线程

当一个程序启动时,只有一个 goroutine 调用 main 函数,称为 主 goroutine, 当 main 函数返回时,
所有 goroutine 都会被终止 (不论其是否运行完成),然后程序退出。

语法规则

关键字 go 启动一个 goroutine (可以理解为在后台运行一个函数), 需要注意的是: 使用 go 启动的函数没有返回值

# 直接调用一个匿名函数
go func() { // 无参数
    // do something ...
}

go func(x int, y bool ...) { // 有参数
    // do something ...
}
# 调用一个已定义的函数
go foo()   // 无参数

go bar(x int, y bool ...)  // 有参数

例子

直接调用一个匿名函数

package main

import (
    "fmt"
    "time"
)

func main() {
    // 3 个 goroutine 是并发运行的,所以顺序不一定是 1, 2, 3
    // 读者可以多运行几次,看看输出结果

    go func() {
        fmt.Println("goroutine 1")
    }()

    go func() {
        fmt.Println("goroutine 2")
    }()

    go func() {
        fmt.Println("goroutine 3")
    }()

    // 这一行代码不可省略
    // 如果省略掉,意味着主进程不等待 3 个 goroutine 执行完成就退出了,也就不会有 goroutine 的输出信息了
    // 读者可以注释掉这行代码,然后运行看看输出结果
    time.Sleep(1 * time.Second)
}

// $ go run main.go
// 输出如下, 3 个 goroutine 是并发运行的,顺序不一定,所以你的输出可能和这里的不一样
/**
  goroutine 3
  goroutine 1
  goroutine 2
*/

调用 time.Sleep() 睡眠等待 3 个 goroutine 执行完成,虽然达到了演示效果,但是有很多潜在问题。
更好的解决方案请看 waitgroup

调用一个已定义的函数

package main

import (
    "fmt"
    "time"
)

func foo() {
    fmt.Println("goroutine foo")
}

func bar() {
    fmt.Println("goroutine bar")
}

func fooBar(s string) {
    fmt.Printf("goroutine %s\n", s)
}

func main() {
    // 3 个 goroutine 是并发运行的,所以顺序不一定是 1, 2, 3
    // 读者可以多运行几次,看看输出结果

    go foo()

    go bar()

    go fooBar("fooBar")

    // 这一行代码不可省略
    // 如果省略掉,意味着主进程不等待 3 个 goroutine 执行完成就退出了,也就不会有 goroutine 的输出信息了
    // 读者可以注释掉这行代码,然后运行看看输出结果
    time.Sleep(1 * time.Second) 
}

// $ go run main.go
// 输出如下, 3 个 goroutine 是并发运行的,顺序不一定,所以你的输出可能和这里的不一样
/**
  goroutine fooBar
  goroutine foo
  goroutine bar
*/

获取并发线程数量

GOMAXPROCS() 获得并发的线程数量,在 CPU 核大于 1 个的情况下,系统会尽可能调度等于核心数的线程并行运行。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Printf("GOMAXPROCS = %d\n", runtime.GOMAXPROCS(0))
}

// $ go run main.go
// 输出如下,笔者的机器 CPU 是 8 核,你的输出可能和这里的不一样
/**
  GOMAXPROCS = 8
*/

扩展阅读

  1. 协程 - 维基百科
  2. 线程 - 维基百科
  3. Go 圣经 - 第 8 章

联系我

0 条评论
请不要发布违法违规有害信息,如发现请及时举报或反馈
还没有人评论呢,速度抢占沙发!
相关文章
  • 服务端 package main import ( "errors" "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" "os" ) // ...

  • 包包名给包及其目录命名时,应该使用简洁、清晰且全小写的名字包的默认导入优先从go的安装目录查找,然后去go path找,找到既停,没找到报错工具go build执行编译操作,有main包的情况下生成可...

  • 往期回顾: Go语言开发小技巧&易错点100例(一) 本期看点(技巧类用【技】表示,易错点用【易】表示): (1)Go Module中对依赖库版本的升级与降级【技】 (2...

  • 1. 前言 所谓的逃逸分析(Escape analysis)是指由编译器决定内存分配的位置吗不需要程序员指定。 函数中申请一个新的对象 如果分配在栈中, 则函数执行结束后可自动将内存回收 如果分配...

  • 一 JWT介绍 JWT 英文名是 Json Web Token ,是一种用于通信双方之间传递安全信息的简洁的、URL安全的表述性声明规范,经常用在跨域身份验证。 JWT 以 JSON 对象的形式安全传...

  • 概述调用 sync/atomic 包即可。错误的并发操作先来看一个错误的示例。通过启动 1000 个 goroutine 来模拟并发调用,在函数内部对变量 number 进行自增操作,那么可能存在的一...

  • 今日记录一下 学习 golang 这门语言遇到的一些比较特殊的细节,供大家参考。    所以,在我们输出内容的时候,可以包含很多的非 ASCII 码字符。实际上,Go 是天生支持 UTF...

  • 文章字数大约1.95万字,阅读大概需要65分钟,建议收藏后慢慢阅读!!! 1. GoLang语言 1.1 Slice Slice底层实现原理 切片是基于数组实现的,它的底层是数组,它自己本身非常小...

  • 最近做的一个项目是采用前后端分离模式写前端,后端是fabric区块链,提供接口,需要使用post方法进行访问。如上一章注册用户,就是需要把用户名、账户信息转换成json形式 使用post方法传给后端区...

  • 我的客服系统使用的Golang+ Gin作为后端服务,所以下面的代码是演示demo 在 Go 语言中使用 Gin 框架实现 WebSocket 的方法如下: 安装 gin-gonic/websoc...

  • 概述标准库 error 数据类型本质上就是一个接口,原型如下:package builtin type error interface { Error() string }例子实现 erro...

  • 本文参与了思否技术征文,欢迎正在阅读的你也加入。前言这是Go常见错误系列的第15篇:interface使用的常见错误和最佳实践。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsa...

  • msf生成的裸马现在已经不行了,加壳也只能加冷门壳了,VMP,Shielden,upx不是失效就是效果很差,所以当下,得用shellcode来免杀了 msfvenom -a x86 --platfo...

  • 概述关键字 goto 可以使程序跳转到指定的位置执行,那么这个位置如何表示呢?使用 标签 来表示 (可以理解为标签就是一个变量)。语法规则标签的名称大小写敏感,可以搭配 for, switch 语句使...

  • 对于无类型常量,可能大家是第一次听说,但这篇我就不放进拾遗系列里了。 因为虽然名字很陌生,但我们每天都在用,每天都有无数潜在的坑被埋下。包括我本人也犯过同样的错误,当时代码已经合并并发布了,当我意识到...

  • hello 大家好呀,我是小楼,这是系列文《Go底层原理剖析》的第三篇,依旧分析 Http 模块。我们今天来看 Go内置的 RPC。说起 RPC 大家想到的一般是框架,Go 作为编程语言竟然还内置了 ...

  • 一 jaeger链路追踪介绍 什么是链路追踪: 分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的...

  • 所有人都听过这样一个歌谣:从前有座山,山里有座庙,庙里有个和尚在讲故事:从前有座山。。。。,虽然这个歌谣并没有一个递归边界条件跳出循环,但无疑地,这是递归算法最朴素的落地实现,本次我们使用Golang...

  • 概述建议先阅读 字符串, 切片 两个小节。由于字符串不可变,如果每次以 重新赋值 的方式改变字符串,效率会非常低,这时应该使用 []byte 类型,[]byte 元素可以被修改。因为 byte 类型是...

  • 官方资料 官方解释: https://pkg.go.dev/cmd/go#hdr-Build_constraints ,go help buildconstraint 也能看到描述 根据官方描述,go...