GO语言
01GO基础-001GO语言简介
01GO基础-002语言环境安装
01GO基础-003Go 语言结构
01GO基础-004Go 语言基础语法1
01GO基础-004Go 语言基础语法2
01GO基础-004Go 语言基础语法3
01GO基础-005Go 语言数据类型
01GO基础-006Go 语言变量
01GO基础-007Go 语言常量
01GO基础-008Go 语言运算符
01GO基础-009条件语句
01GO基础-010循环语句
01GO基础-011函数
01GO基础-012变量作用域
01GO基础-013数组
01GO基础-014指针
01GO基础-015结构体
01GO基础-016切片
01GO基础-017范围(Range)
01GO基础-018Map
01GO基础-019递归函数
01GO基础-020类型转换
01GO基础-021接口
01GO基础-022异常处理
01GO基础-023并发
01GO基础-024strings
01GO基础-025可变参数
01GO基础-026接口2
01GO基础-027异常处理2
01GO基础-028sync包详解
01GO基础-029Context
02GO进阶001包
02GO进阶002init()函数
02GO进阶003包的注意点
02GO进阶003使用go module导入本地包
02GO进阶004 time包
02GO进阶005 file操作
02GO进阶006 io操作
02GO进阶007 os包(文件 I/O、文件属性、目录与链接、创建和移除链接)
02GO进阶008复制文件
02GO进阶009断点续传
02GO进阶010 bufio包
02GO进阶011ioutil包
02GO进阶012遍历文件夹
02GO进阶013并发编程介绍
02GO进阶014Goroutine协程
02GO进阶015 GPM
02GO进阶016 runtime包
02GO进阶017 Channel
02GO进阶018 Goroutine池
02GO进阶019 定时器
02GO进阶020 select
02GO进阶021并发安全和锁
02GO进阶022sync
02GO进阶023原子操作
02GO进阶024 GMP原理与调度
02GO进阶025爬虫小案例
02GO进阶026 面向对象-匿名字段
02GO进阶026 面向对象-接口
02GO进阶027网络编程-互联网协议介绍
02GO进阶027网络编程-socket
02GO进阶027网络编程-http编程
02GO进阶027网络编程-websocket编程
02GO进阶028数据操作-MYSQL
02GO进阶028数据操作-REDIS
02GO进阶028数据操作-RTCD
02GO进阶028数据操作-ZOOKEEPER
02GO进阶028数据操作-KAFKA
02GO进阶028数据操作-RabbitMQ
02GO进阶028数据操作-ElasticSearch
02GO进阶028数据操作-NSQ
02GO进阶028数据操作-memcached
02GO进阶028数据操作-GORM
02GO进阶029beego框架-安装
02GO进阶029beego框架-快速入门
02GO进阶029beego框架-MVC架构介绍-controller设计-参数配置
02GO进阶029beego框架-MVC架构介绍-controller设计-路由设置
02GO进阶029beego框架-MVC架构介绍-controller设计-控制器函数
02GO进阶029beego框架-MVC架构介绍-controller设计-XSRF过滤
02GO进阶029beego框架-MVC架构介绍-controller设计-请求数据处理
02GO进阶029beego框架-MVC架构介绍-controller设计-Session控制
02GO进阶029beego框架-MVC架构介绍-controller设计-过滤器
02GO进阶029beego框架-MVC架构介绍-controller设计-Flash数据
02GO进阶029beego框架-MVC架构介绍-controller设计-URL构建
02GO进阶029beego框架-MVC架构介绍-controller设计-多种格式数据输出
02GO进阶029beego框架-MVC架构介绍-controller设计-表单数据验证
02GO进阶029beego框架-MVC架构介绍-controller设计-错误处理
02GO进阶029beego框架-MVC架构介绍-controller设计-日志处理
02GO进阶029beego框架-MVC架构介绍-model设计-概述
02GO进阶029beego框架-MVC架构介绍-model设计-CRUD操作
02GO进阶029beego框架-MVC架构介绍-model设计-高级查询
02GO进阶029beego框架-MVC架构介绍-model设计-原生SQL查询
02GO进阶029beego框架-MVC架构介绍-model设计-构造查询
02GO进阶029beego框架-MVC架构介绍-model设计-事务处理
02GO进阶029beego框架-MVC架构介绍-model设计-模型定义
02GO进阶029beego框架-MVC架构介绍-model设计-命令模式
02GO进阶029beego框架-MVC架构介绍-model设计-测试用例
02GO进阶029beego框架-MVC架构介绍-view设计-beego 模板语法指南
02GO进阶029beego框架-MVC架构介绍-view设计-模板处理
02GO进阶029beego框架-MVC架构介绍-view设计-其他
本文档使用 MrDoc 发布
-
+
首页
01GO基础-023并发
## 并发 并发是指程序同时执行多个任务的能力。 Go 语言支持并发,通过 goroutines 和 channels 提供了一种简洁且高效的方式来实现并发。 ### Goroutines: Go 中的并发执行单位,类似于轻量级的线程。 Goroutine 的调度由 Go 运行时管理,用户无需手动分配线程。 使用 go 关键字启动 Goroutine。 Goroutine 是非阻塞的,可以高效地运行成千上万个 Goroutine。 ### Channel: Go 中用于在 Goroutine 之间通信的机制。 支持同步和数据共享,避免了显式的锁机制。 使用 chan 关键字创建,通过 <- 操作符发送和接收数据。 ### Scheduler(调度器): Go 的调度器基于 GMP 模型,调度器会将 Goroutine 分配到系统线程中执行,并通过 M 和 P 的配合高效管理并发。 G:Goroutine。 M:系统线程(Machine)。 P:逻辑处理器(Processor)。 ## Goroutine goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。 goroutine 语法格式: `go 函数名( 参数列表 )` 例如: `go f(x, y, z)` 开启一个新的 goroutine: `f(x, y, z)` Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。 实例 ```go package main import ( "fmt" "time" ) func sayHello() { for i := 0; i < 5; i++ { fmt.Println("Hello") time.Sleep(100 * time.Millisecond) } } func main() { go sayHello() // 启动 Goroutine for i := 0; i < 5; i++ { fmt.Println("Main") time.Sleep(100 * time.Millisecond) } } ``` 执行以上代码,你会看到输出的 Main 和 Hello。输出是没有固定先后顺序,因为它们是两个 goroutine 在执行: Main Hello Main Hello ... ## 通道(Channel) 通道(Channel)是用于 Goroutine 之间的数据传递。 通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。 使用 make 函数创建一个 channel,使用 <- 操作符发送和接收数据。如果未指定方向,则为双向通道。 ```go ch <- v // 把 v 发送到通道 ch v := <-ch // 从 ch 接收数据 // 并把值赋给 v ``` 声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建: `ch := make(chan int)` 注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。 以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和: 实例 ```go package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 把 sum 发送到通道 c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从通道 c 中接收 fmt.Println(x, y, x+y) } ``` 输出结果为: -5 17 12 ## 通道缓冲区 通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小: `ch := make(chan int, 100)` 带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。 不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。 注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。 实例 ```go package main import "fmt" func main() { // 这里我们定义了一个可以存储整数类型的带缓冲通道 // 缓冲区大小为2 ch := make(chan int, 2) // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据 // 而不用立刻需要去同步读取数据 ch <- 1 ch <- 2 // 获取这两个数据 fmt.Println(<-ch) fmt.Println(<-ch) } ``` 执行输出结果为: 1 2 ## Go 遍历通道与关闭通道 Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下: `v, ok := <-ch` 如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。 实例 ```go package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个 // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据 // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不 // 会结束,从而在接收第 11 个数据的时候就阻塞了。 for i := range c { fmt.Println(i) } } ``` 执行输出结果为: 0 1 1 2 3 5 8 13 21 34 ## Select 语句 select 语句使得一个 goroutine 可以等待多个通信操作。select 会阻塞,直到其中的某个 case 可以继续执行: 实例 ```go package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) } ``` 以上代码中,fibonacci goroutine 在 channel c 上发送斐波那契数列,当接收到 quit channel 的信号时退出。 执行输出结果为: 0 1 1 2 3 5 8 13 21 34 quit ## 使用 WaitGroup sync.WaitGroup 用于等待多个 Goroutine 完成。 同步多个 Goroutine: 实例 ```go package main import ( "fmt" "sync" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // Goroutine 完成时调用 Done() fmt.Printf("Worker %d started\n", id) fmt.Printf("Worker %d finished\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 3; i++ { wg.Add(1) // 增加计数器 go worker(i, &wg) } wg.Wait() // 等待所有 Goroutine 完成 fmt.Println("All workers done") } ``` 以上代码,执行输出结果如下: Worker 1 started Worker 1 finished Worker 2 started Worker 2 finished Worker 3 started Worker 3 finished All workers done ## 高级特性 ### Buffered Channel: 创建有缓冲的 Channel。 `ch := make(chan int, 2)` Context: 用于控制 Goroutine 的生命周期。 `context.WithCancel、context.WithTimeout。` Mutex 和 RWMutex: sync.Mutex 提供互斥锁,用于保护共享资源。 ```go var mu sync.Mutex mu.Lock() // critical section mu.Unlock() ``` ## 并发编程小结 Go 语言通过 Goroutine 和 Channel 提供了强大的并发支持,简化了传统线程模型的复杂性。配合调度器和同步工具,可以轻松实现高性能并发程序。 Goroutines 是轻量级线程,使用 go 关键字启动。 Channels 用于 goroutines 之间的通信。 Select 语句 用于等待多个 channel 操作。 ## 常见问题 死锁 (Deadlock): 示例:所有 Goroutine 都在等待,但没有任何数据可用。 解决:避免无限等待、正确关闭通道。 数据竞争 (Data Race): 示例:多个 Goroutine 同时访问同一变量。 解决:使用 Mutex 或 Channel 同步访问。
admin
2024年12月20日 14:36
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码