创建一个协程非常简单
Golang中的并发是函数相互独立运行的能力。
Goroutines是并发运行的函数。
Golang提供了Goroutines作为并发处理操作的一种方式。
创建一个协程非常简单,就是在一个任务函数前面添加一个go关键字:
package main
import (
"fmt"
"time"
)
func show(msg string) {
for i := 1; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
go show("java")
// 在main协程中执行,如果它前面也添加go,程序没有输出
show("golang")
fmt.Println("end...") // 主函数退出,程序就结束了
}
PS E:\TEXT\test> go run .
msg: golang
msg: java
msg: java
msg: golang
msg: golang
msg: java
msg: golang
msg: java
end...
PS E:\TEXT\test>
查看 go 关键字去掉的运行结果
package main
import (
"fmt"
"time"
)
func show(msg string) {
for i := 1; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
show("java")
show("golang") // 在main协程中执行,如果它前面也添加go,程序没有输出
fmt.Println("end...") // 主函数退出,程序就结束了
}
PS E:\TEXT\test> go run .
msg: java
msg: java
msg: java
msg: java
msg: golang
msg: golang
msg: golang
msg: golang
end...
PS E:\TEXT\test>
通道的机制,用于在goroutine之间共享数据
Go提供了一种称为通道的机制,用于在goroutine之间共享数据。当您作为goroutine执行并发活动时,需要在goroutine之间共享资源或数据,通道充当goroutine之间的管道(管道)并提供一种机制来保证同步交换。
需要在声明通道时指定数据类型。我们可以共享内置、命名、结构和引用类型的值和指针。数据在通道上传递:在任何给定时间只有一个goroutine可以访问数据项:因此按照设计不会发生数据竞争。
根据数据交换的行为,有两种类型的通道:无缓冲通道和缓冲通道。无缓冲通道用于执行goroutine之间的同步通信,而缓冲通道用于执行异步通信。无缓冲通道保证在发送和接收发生的瞬间两个goroutine之间的交换。缓冲通道没有这样的保证。
通道由make函数创建,该函数指定chan关键字和通道的元素类型。
创建无缓冲和缓冲通道的代码块
Unbuffered := make(chan int) // 整型无缓冲通道
buffered := make(chan int, 10) // 整型有缓冲通道
使用内置函数 make 创建无缓冲和缓冲通道。
make 的第一个参数需要关键字chan,然后是通道允许交换的数据类型。
<-
goroutine1 := make(chan string, 5) // 字符串缓冲通道
goroutine1 <- "Australia" // 通过通道发送字符串
一个包含 5 个值的缓冲区的字符串类型的 goroutine1 通道。
然后我们通过通道发送字符串“Australia”。
从通道接收值的代码块:
data := <- goroutine1 // 从通道接收字符串
<-
无缓冲通道是同步的定义
在无缓冲通道中,接收到任何值之前没有能力保存它。
在这种类型的通道中,发送和接收 goroutine 在任何发送或接收操作完成之前的同一时刻都准备就绪。
如果两个 goroutine 没有在同一时刻准备好,则通道会让执行其各自发送或接收操作的 goroutine 首先等待。
同步是通道上发送和接收之间交互的基础。
没有另一个就不可能发生。
有缓冲通道是异步的定义
在缓冲通道中,有能力在接收到一个或多个值之前保存它们。
在这种类型的通道中,不要强制 goroutine 在同一时刻准备好执行发送和接收。
当发送和接收阻塞时也有不同的条件。只有当通道中没有要接收的值时,接收才会阻塞。仅当没有可用缓冲区来放置正在发送的值时,发送才会阻塞。
通道的发送和接收特性
- 对于同一个通道,发送操作之间是互斥的。接收操作之间也是互斥的。
- 发送操作和接收操作中对元素值的处理都是不可分割的。
- 发送操作在完全完成之前会被阻塞。接收操作也是如此。
package main
import (
"fmt"
"math/rand"
"time"
)
// 创建int类型通道,只能传入int类型值
var values = make(chan int)
func send(rng *rand.Rand) {
value := rng.Intn(10)
fmt.Printf("send: %v\n", value)
// time.Sleep(time.Second * 5)
values <- value
}
func main() {
// 使用时间种子创建随机数生成器
seed := time.Now().UnixNano()
rng := rand.New(rand.NewSource(seed))
// 从通道接收值
defer close(values)
go send(rng)
fmt.Println("wait...")
value := <-values
fmt.Printf("receive: %v\n", value)
fmt.Println("end...")
}
PS E:\TEXT\test> go run .
wait...
send: 5
receive: 5
end...
PS E:\TEXT\test>
WaitGroup实现同步
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // gorontine结束就登记-1
fmt.Printf("i: %v\n", i)
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) // 启动一个goroutine就登记+1
go hello(i)
}
wg.Wait() // 等待所有登记的goroutine都结束
}
PS E:\TEXT\test> go run .
i: 9
i: 0
i: 2
i: 5
i: 3
i: 4
i: 7
i: 8
i: 6
i: 1
PS E:\TEXT\test>