go 语言中的并发特性

★本节源码位置 https://github.com/golang-minibear2333/golang/blob/master/4.concurrent/4.1-goroutine/ ”

以前我们写并发的程序一般是用多线程来实现,自己维护一个线程池,在恰当的时候创建、销毁、分配资源。

go 在并发方面为我们提供了一个语言级别的支持, goroutinechan 相互配合,这决定了他的先天优势。

goroutine 的概念类似于线程, Go 程序运行时会自动调度和管理,系统能智能地将 goroutine 中的任务合理地分配给 CPU , 让这些任务尽量并发运作。

他和线程对比

从使用上讲

  • 比线程更轻量级,可以创建十万、百万不用担心资源问题。
  • chan 搭配使用,实现高并发, goroutine 之间传输数据更方便。
  • 如果访问同一个数据块,要小心数据竞态问题、共享锁还是互斥锁的选择问题、并发操作的数据同步问题(后面会说)

从其实现上讲

  • 从资源上讲,线程的栈内存大小一般是固定的一般为 2MB ,虽然这个数值可以设置,但是 太大了浪费,太小了容易不够用, 而 goroutine 栈内存是可变的,初始一般为 2KB ,随着需求可以扩大达到 1GB。所以 goroutine 十分的轻量级,且能满足不同的需求。
  • 从调度上讲,线程的调度由 OS 的内核完成;线程的切换需要 CPU 寄存器内存的数据交换 ,从而切换不同的线程上下文。其触发方式为 CPU时钟 , 而 goroutine 的调度则比较轻量级,由自身的调度器完成。
  • 协程同线程的关系,有些类似于 线程同进程的关系。

创建与使用

创建一个 goroutine ,只需要在函数前加一个 go 关键字就成了。

go 函数名(参数)

看一个 dome

func quickFun(){
 fmt.Println("maybe you can's see me!")
}

func main(){
 go quickFun() // 创建了一个 goroutine
 fmt.Println("hey")
 time.Sleep(time.Second)
}
  • goroutinemain 主线程同时运行
  • main 运行结束会暴力终止所有协程,所以上面的程序多等待了 1 秒
  • Go 程序从 main 包的 main() 函数开始,在程序启动时, Go 程序就会为 main() 函数创建一个默认的 goroutine

输出

hey
maybe you can's see me!

对,就是这么简单,如果你的函数只在这里使用,也可以用匿名函数来创建 goroutine

func main(){
 go func() {
  fmt.Println("hello ")
 }()
 time.Sleep(time.Second) //main运行结束会暴力终止所有协程,所以这里先等待1秒
}

PS: 和线程不同,goroutine没有唯一的id,所以我们没办法专门q针对某个协程进行操作。

goroutine

goroutine 是 Go 语言并行设计的核心。goroutine 是一种比线程更轻量的实现,十几个 goroutine 可能在底层就是几个线程。实际上是 Go 在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理。

使用 goroutine 只需要简单的在需要执行的函数前添加 go 关键字即可。当执行 goroutine 时候,Go 语言立即返回,接着执行剩余的代码,不会阻塞主线程。

下面我们通过一小段代码来讲解 go 的使用:

//首先我们先实现一个 Add()函数
func Add(a, b int) {
c := a + b
fmt.Println(c)
}

go Add(1, 2) //使用go关键字让函数并发执行

Go 的并发执行就是这么简单,当在一个函数前加上 go 关键字,该函数就会在一个新的 goroutine 中并发执行,当该函数执行完毕时,这个新的 goroutine 也就结束了。不过需要注意的是,如果该函数具有返回值,那么返回值会被丢弃。所以什么时候用 go 还需要酌情考虑。

接着我们通过一个案例来体验一下 Go 的并发到底是怎么样的。新建源文件 goroutine2.go,输入以下代码:

package main

import "fmt"

func Add(a, b int) {
 c := a + b
 fmt.Println(c)
}

func main() {
 for i := 0; i < 10; i++ {
  go Add(i, i)
 }
}

执行 goroutine.go 文件会发现屏幕上什么都没有,但程序并不会报错,这是什么原因呢?

原来当主程序执行到 for 循环时启动了 10 个 goroutine,然后主程序就退出了,而启动的 10 个 goroutine 还没来得及执行 Add() 函数,所以程序不会有任何输出。也就是说主 goroutine 并不会等待其他 goroutine 执行结束。

Go 语言提供的信道(channel)就是专门解决并发通信问题的,下一节我们将详细介绍。

小结

学 go 语言必学并发,通过本节我们知道了协程是非常容易创建的,而且他非常轻量只占用 4k,其他语言很容易就上MB

协程的使用还要配合数据传输,生产者消费者模型,关于协程的调度,我们后续再说。

另外并发 bug 的定位和解决是老大难的问题了,平时就要注意的良好的代码风格和编程习惯。

PS: 后续勘误请点击阅读原文,进入开源电子书

原文地址:https://cloud.tencent.com/developer/article/1849709

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340