GO语言基础笔记(四):并发编程基础
2023-12-24 05:57:38
目录
Goroutines
-
定义与特点:
- Goroutines是Go语言中实现并发的基本单位。
- 它比传统的线程更轻量级,拥有更小的内存占用和更快的启动时间。
- 在Go程序中,您可以轻松地启动成千上万的Goroutines。
-
使用方法:
- 使用
go
关键字后跟一个函数调用,即可启动一个Goroutine。 - 例如:
go myFunction()
- 使用
package main
import (
"fmt"
"time"
)
// 定义一个简单的函数
func printNumbers(prefix string) {
for i := 1; i <= 5; i++ {
fmt.Println(prefix, i)
// 休眠一段时间,以模拟实际操作中的耗时
time.Sleep(time.Millisecond * 500)
}
}
func main() {
// 使用 go 关键字启动一个新的 Goroutine
go printNumbers("Goroutine")
// 主Goroutine也执行相同的函数
printNumbers("Main")
// 等待足够长的时间以确保Goroutine完成
// 注意:这不是同步Goroutines的推荐方式
// 后续课程将介绍更好的方法(如WaitGroup或Channel)
time.Sleep(time.Second * 3)
fmt.Println("主函数执行完毕")
}
- 函数定义:
printNumbers
函数接受一个字符串参数prefix
,并打印出该前缀下的数值序列。- 启动Goroutine:使用
go
关键字启动printNumbers
函数作为一个新的Goroutine。这意味着printNumbers("Goroutine")
将并行执行。- 主Goroutine:主函数
main
本身也是一个Goroutine。在这里,它调用printNumbers("Main")
,同样执行数值打印操作。- 并行执行:您会看到,"Goroutine"和"Main"的输出是交替出现的,显示了两个Goroutine是同时运行的。
- 等待结束:使用
time.Sleep
暂时等待Goroutine完成。这是为了演示目的;实际应用中我们会使用更加精确的同步机制。
- 在实际应用中,我们通常不使用
time.Sleep
来等待Goroutines完成,而是使用像通道(Channels)或sync.WaitGroup
这样的同步机制。 - 这个示例的目的是简单地展示Goroutines的并发执行。在后续课程中,我们将探讨更高级的同步技术和并发模式。
?
通道(Channel)
-
定义与作用:
- Channel是Go语言中的一种内置类型,用于在Goroutines之间安全地传递数据。
- 它可以帮助解决Goroutines之间的同步问题。
-
类型与使用:
- 有缓冲和无缓冲两种类型的Channel。
- 创建示例:
ch := make(chan int)
(无缓冲)或ch := make(chan int, 5)
(有缓冲,容量为5)。 - 数据传递:使用
<-
操作符向Channel发送或接收数据。
?
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个无缓冲的Channel
ch := make(chan string)
// 启动一个Goroutine,发送数据到Channel
go func() {
fmt.Println("Goroutine开始发送数据")
ch <- "从Goroutine传来的消息"
}()
// 模拟延时,表示主Goroutine正在处理其他任务
time.Sleep(2 * time.Second)
// 从Channel接收数据
message := <-ch
fmt.Println("接收到数据:", message)
// 创建一个有缓冲的Channel,容量为2
bufferedCh := make(chan int, 2)
// 向有缓冲的Channel发送数据,不会立即阻塞
bufferedCh <- 1
bufferedCh <- 2
// 从有缓冲的Channel接收数据
fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
}
- 无缓冲Channel:
ch := make(chan string)
创建了一个无缓冲的Channel,用于传递字符串类型的数据。- 发送数据到Channel:Goroutine中使用
ch <- "从Goroutine传来的消息"
向Channel发送数据。此时,如果没有接收者,发送操作会阻塞。- 接收Channel数据:主Goroutine中使用
message := <-ch
接收数据。接收操作会阻塞直到有数据到达。- 有缓冲Channel:
bufferedCh := make(chan int, 2)
创建了一个容量为2的有缓冲Channel。- 向有缓冲Channel发送数据:发送操作在Channel未满时不会阻塞。
- 从有缓冲Channel接收数据:即使发送Goroutine已经结束,数据仍然可以从Channel中被接收。
- 无缓冲Channel在没有接收者时,发送操作会阻塞;有缓冲Channel则在缓冲区满时才会阻塞。
- Channel的正确使用是并发编程中非常重要的部分,需要仔细处理发送和接收的同步问题。
代码示例
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// 数据处理函数
// 模拟接收一个整数,经过处理后发送到输出Channel
func processData(id int, data int, out chan<- string) {
// 模拟数据处理耗时
time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
result := fmt.Sprintf("Goroutine %d 处理结果:%d", id, data*2) // 假设处理是简单的乘以2
out <- result
}
func main() {
// 初始化随机种子
rand.Seed(time.Now().UnixNano())
// 创建一个无缓冲的Channel
dataCh := make(chan string)
// 使用WaitGroup等待所有Goroutines完成
var wg sync.WaitGroup
// 启动多个Goroutines进行数据处理
const numGoroutines = 5 // Goroutines的数量
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
data := rand.Intn(100) // 生成一个随机数作为数据
processData(id, data, dataCh)
}(i)
}
// 启动一个Goroutine用于接收所有处理结果
go func() {
wg.Wait()
close(dataCh)
}()
// 从Channel中读取并打印每个Goroutine的处理结果
for result := range dataCh {
fmt.Println(result)
}
fmt.Println("所有Goroutine处理完成")
}
-
processData
函数:每个Goroutine都会调用这个函数,它接收一个整数数据,处理后(这里简化为乘以2的操作),将结果发送到输出Channel。 -
启动多个Goroutines:循环中创建了多个Goroutines,每个都调用
processData
函数处理数据。 -
使用
sync.WaitGroup
:WaitGroup
用于等待所有Goroutine完成其工作。每启动一个Goroutine,就调用wg.Add(1)
,每个Goroutine完成时调用wg.Done()
。 -
关闭Channel:在所有Goroutines完成后,关闭数据Channel。这是通过在另一个Goroutine中调用
wg.Wait()
和close(dataCh)
实现的。 -
读取Channel数据:主Goroutine循环读取Channel中的数据,并打印结果。
文章来源:https://blog.csdn.net/weixin_44120785/article/details/135177686
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!