全网注释第二全的GO教程并发(Goroutine)

2023年 8月 7日 48.3k 0

持续更新: github.com/Zhouchaowen… 感谢 star

系列文章-包(Package)
系列文章-变量与常量(Var)
系列文章-函数(Function)
系列文章-流程控制(For,If,Switch,defer)
系列文章-结构体(Struct)
系列文章-数组与切片(Array&Slice)
系列文章-映射(Map)
系列文章-接口(Interface)

Goroutine

GoroutineGo 语言中提供并发处理的方式。在学习Goroutine之前,我们先了解下什么是并发?什么是并行。

  • 并发:一段时间内执行多个任务,这意味着仅需一个物理核心就能实现。

并发本质是我们的感知问题,就像电影闪电侠一样,普通人1s只能做一件事,而闪电侠1s可以做很多件事情,这导致我们产生了错觉,觉得闪电侠在1s内同时做这些事;

其实闪电侠只是在0.1s做了A事,在0.2s做了B事情,在0.3s做了C事,只是闪电侠做的太快了,普通人类没法在短短的0.1s感知到而已。当1s后感知到时,发现A,B,C都做完了,这就产生了闪电侠在1s内同时做这些事的错觉。

在现实中CPU的执行是非常快的,我们通过将CPU的执行时间划分为不同的时间片来执行任务。下图中 CPU在1 s内并发的运行了3个任务(A,B,C),这里将CPU的1s执行时间划分为了5个时间片,用来分别执行这些任务。

9-1.cpu1.png

图中在时间片1内CPU加载任务A并执行,当时间片1执行完后,CPU会将任务A移出然后开始时间片2,在时间片2中CPU会加载任务B并执行,接下来会按照这种方式依次执行任务C,任务B,任务A;为什么还会执行任务B和任务A?因为任务AB可能在时间片1和2没有执行完就被移出了,所以在后面的时间片又被加载执行。

CUP执行时间片中,无论任务A,B,C本身是否执行完,当时间片耗尽后都会被移出CPU,这个加载和移出的过程叫做上下文切换。

  • 并行:同一时刻执行多个任务,这意味着至少需要2个物理核心才能实现。

并行指严格意义上的同一时刻执行2个或2个以上的任务,这取决于你的CPU核心数。

9-1.cpu2.png

上图中一个CPU至少有两个物理核心Core,每个核心上都运行着不同的任务。任务A和任务B分别运行在时间片1和时间片2上并且都运行在Core-1的核心上,这意味着任务A和任务B是并发运行的;

而任务A和任务F都运行在时间片1上(代表同一时刻),并且他们分别运行在Core-1Core-2的核心上,所以他们是并行运行的。

为什么说一个CPU至少有两个物理核心Core

因为现代的一颗CPU上可能会有多个核心,又因为超线程技术,一个物理核心可以当做两个虚拟核心使用,所以理论上一个核心在同一时刻可以并行执行2个任务。而一般一个CPU具有4个核心,意味着这个CPU同一时刻可以并行运行8个任务(4*2=8)。

9-1.cpu3.png

上文中的任务具体代表什么喃?

上文的一个任务代表一个线程,这些线程在不同的CPU时间片上运行,这些线程都是由内核进行调度。而我们在Go中的Goroutine 也可以看作是一个轻量级线程,它是由GoGoroutine 调度器来调度的,具体的细节可以看这两篇文章来了解:Golang 调度器 GMP 原理与调度全分析和GMP 并发调度器深度解析

我们的一个Go程序可以启动成百上千个 GoroutineGoroutine 的启动非常快,只需要几纳秒的时间, 而且不需要开发者手动进行线程调度,都由 GoGoroutine调度器自动完成,接下来我们就来了解一下Goroutine

目录

  • Goroutine 基础用法
  • Goroutinesync.WaitGroup 的使用
  • Goroutine 小实验: 并发的下载图片
  • Goroutine 的并发安全问题
    • 原子操作
    • 加锁保护

Goroutine基础

Golang中想要并发的执行一段逻辑可以通过go关键字+匿名函数或有名函数实现, 代码如下:

// 匿名函数
go func() {
    // goroutine 执行的代码
}()


// 有名函数
func test(){
  fmt.Printf("golang tutorialn")
}
go test()

一个go func()会启动一个后台并发任务, 大概流程是通过go关键字将这个func()打包成一个任务, 然后提交给Golang的并发调度器, 并发调度器会根据一定策略来执行这些任务。具体的细节可以看这两篇文章来了解:Golang 调度器 GMP 原理与调度全分析和GMP 并发调度器深度解析

如下实现并发打印两个数组内的数据:

package main

import (
"fmt"
"time"
)

// 并发与并行:https://gfw.go101.org/article/control-flows-more.html

// 使用 goroutine 打印数据
func main() {
language := []string{"golang", "java", "c++", "python", "rust", "js"}
tutorial := []string{"入门", "初级", "中级", "高级", "专家"}

// Go 程(goroutine)是由 Go 运行时管理的轻量级线程
// 在函数调⽤语句前添加 go 关键字,就可创建一个 goroutine
go listLanguage(language) // 通过goroutine启动该函数
go listTutorial(tutorial)

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论