GO语言基础教程22——并发

简介: 串行:我们都是先读小学,小学毕业后再读初中,读完初中再读高中。并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天)。

GO语言基础教程22——并发


GO语言基础教程22——并发


基本概念


串行、并发与并行


串行:我们都是先读小学,小学毕业后再读初中,读完初中再读高中。


并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。


并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天)。


进程、线程和协程


进程(process):程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。


线程(thread):操作系统基于进程开启的轻量级进程,是操作系统调度执行的最小单位。


协程(coroutine):非操作系统提供而是由用户自行创建和控制的用户态‘线程’,比线程更轻量级。


goroutine


Goroutine 是 Go 程序中最基本的并发执行单元。每一个 Go 程序都至少包含一个 goroutine——main goroutine,当 Go 程序启动时它会自动创建。


在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能——goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个 goroutine 去执行这个函数就可以了,就是这么简单粗暴。


go关键字


Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个 goroutine ,从而让该函数或方法在新创建的 goroutine 中执行。

go toGo()  // 创建一个新的 goroutine 运行函数toGo

匿名函数也支持使用go关键字创建 goroutine 去执行。

go func(){
  // ...
}()


启动单个goroutine

package main
import (
  "fmt"
  "time"
)
func hello() {
  fmt.Println("hello")
}
func main() {
  go hello()
  fmt.Println("你好")
  time.Sleep(time.Second)//不加这句的话会只输出你好
}

在 Go 程序启动时,Go 程序就会为 main 函数创建一个默认的 goroutine 。在上面的代码中我们在 main 函数中使用 go 关键字创建了另外一个 goroutine 去执行 hello 函数,而此时 main goroutine 还在继续往下执行,我们的程序中此时存在两个并发执行的 goroutine。当 main 函数结束时整个程序也就结束了,同时 main goroutine 也结束了,所有由 main goroutine 创建的 goroutine 也会一同退出。也就是说我们的 main 函数退出太快,另外一个 goroutine 中的函数还未执行完程序就退出了,导致未打印出“hello”。


启动多个goroutine

package main
import (
  "fmt"
  "sync"
)
//开启计数器多次执行上面的代码会发现每次终端上打印数字的顺序都不一致。这是因为10个 goroutine 是并发执行的,而 goroutine 的调度是随机的。
## channel
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 `<-` 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
var wg sync.WaitGroup
func hello(i int) {
  defer wg.Done() // goroutine结束就登记-1
  fmt.Println("hello", i)
}
func main() {
  for i := 0; i < 10; i++ {
    wg.Add(1) // 启动一个goroutine就登记+1
    go hello(i)
  }
  wg.Wait() // 等待所有登记的goroutine都结束
}

多次执行上面的代码会发现每次终端上打印数字的顺序都不一致。这是因为10个 goroutine 是并发执行的,而 goroutine 的调度是随机的。


channel


通道(channel)是用来传递数据的一个数据结构。

var 变量名称 chan 元素类型

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。


channel零值


未初始化的通道类型变量其默认零值是nil

var ch chan int
fmt.Println(ch) // <nil>


初始化channel


声明的通道类型变量需要使用内置的make函数初始化之后才能使用。具体格式如下:

make(chan 元素类型, [缓冲大小])


channel操作


通道共有发送(send)、接收(receive)和关闭(close)三种操作。而发送和接收操作都使用<-符号。


创建一个通道

ch := make(chan int)

发送

ch <- 100// 把100送到ch中

接收

x := <- ch // 从ch中接收值并赋值给变量x
<-ch       // 从ch中接收值,忽略结果

关闭

close(ch)


无缓冲区通道


无缓冲的通道又称为阻塞的通道。我们来看一下如下代码片段。

func main() {
  ch := make(chan int)
  ch <- 10
  fmt.Println("发送成功")
}

该段代码在执行时会报错,因为我们使用ch := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有接收方能够接收值的时候才能发送成功,否则会一直处于等待发送的阶段。


解决办法


开启一个goroutine接收值。

func recv(c chan int) {
  ret := <-c
  fmt.Println("接收成功", ret)
}
func main() {
  ch := make(chan int)
  go recv(ch) // 创建一个 goroutine 从通道接收值
  ch <- 10
  fmt.Println("发送成功")
}

使用无缓冲通道进行通信将导致发送和接收的 goroutine 同步化。因此,无缓冲通道也被称为同步通道


有缓冲区通道


还有另外一种解决上面问题的方法,那就是使用有缓冲区的通道。我们可以在使用 make 函数初始化通道时,可以为其指定通道的容量,例如:

func main() {
  ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
  ch <- 10
  fmt.Println("发送成功")
}

只要通道的容量大于零,那么该通道就属于有缓冲的通道,通道的容量表示通道中最大能存放的元素数量。当通道内已有元素数达到最大容量后,再向通道执行发送操作就会阻塞,除非有从通道执行接收操作。就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。


遍历通道


通常我们会选择使用for range循环从通道中接收值,当通道被关闭后,会在通道内的所有值被接收完毕后会自动退出循环。上面那个示例我们使用for range改写后会很简洁。

func f3(ch chan int) {
  for v := range ch {
    fmt.Println(v)
  }
}


单向通道

<- chan int // 只接收通道,只能接收不能发送
chan <- int // 只发送通道,只能发送不能接收


相关文章
|
5天前
|
Go
go的并发初体验、加锁、异步
go的并发初体验、加锁、异步
6 0
|
5天前
|
Go
go语言map、实现set
go语言map、实现set
12 0
|
5天前
|
Go
go语言数组与切片
go语言数组与切片
14 0
|
1天前
|
JSON 算法 测试技术
在go语言中调试程序
【6月更文挑战第29天】Go语言内置`testing`包支持单元测试、基准测试和模糊测试。`go test`命令可执行测试,如`-run`选择特定测试,`-bench`运行基准测试,`-fuzz`进行模糊测试。
12 2
在go语言中调试程序
|
7天前
|
存储 中间件 Go
在go语言服务中封装路由和示例
【6月更文挑战第23天】本文介绍golang后端按协议处理、中间件(一次性与每次请求执行)划分、以及服务架构Controller、Logic/Service、DAO/Repository和Routers划分。代码仓库在GitHub上提供。使用框架简化了交互和处理。后续章节深入探讨服务构建。
105 5
在go语言服务中封装路由和示例
|
4天前
|
Devops Go 云计算
Go语言发展现状:历史、应用、优势与挑战
Go语言发展现状:历史、应用、优势与挑战
|
5天前
|
Go
go语言的hello,world
go语言的hello,world
10 1
|
3天前
|
编译器 Go C++
必知的技术知识:go语言快速入门教程
必知的技术知识:go语言快速入门教程
|
4天前
|
编译器 Go 开发者
|
5天前
|
Java Go Windows
go语言实现加减法出题器(再也不用担心孩子学习了)
go语言实现加减法出题器(再也不用担心孩子学习了)
10 0