Golang最擅长的就是并发编程,使用Golang可以很方便的进行并发编程。先看一段普通的代码
package main
import (
"fmt"
"time"
)
func Foo(i int) {
fmt.Printf("%d will sleep\n", i)
time.Sleep(5 * time.Second)
fmt.Printf("%d wake up\n", i)
}
func main() {
for i := 0; i < 5; i++ {
Foo(i)
}
}
输出为
0 will sleep
0 wake up
1 will sleep
1 wake up
2 will sleep
2 wake up
3 will sleep
3 wake up
4 will sleep
4 wake up
大概要执行25秒的时间,因为我们的foo中休眠5s,5次循环,去掉其他时间至少需要25秒时间执行完成。现在,我们希望并发调用Foo函数,很简单,使用go关键字创建协程(协程比线程的执行更加轻量级)
package main
import (
"fmt"
"time"
)
func Foo(i int) {
fmt.Printf("%d will sleep\n", i)
time.Sleep(5 * time.Second)
fmt.Printf("%d wake up\n", i)
}
func main() {
for i := 0; i < 5; i++ {
go Foo(i)
}
time.Sleep(10 * time.Second)
}
在调用Foo前使用了go,这样就会并发执行,最后添加休眠10秒,是防止主进程结束,协程也被销毁。下面的例子使用通道我们可以看到更优雅的解决方法。
package main
import (
"fmt"
"time"
)
func Foo(i int, ch chan int) {
fmt.Printf("%d will sleep\n", i)
time.Sleep(5 * time.Second)
fmt.Printf("%d wake up\n", i)
ch <- 1
}
func main() {
ch := make(chan int)
for i := 0; i < 5; i++ {
go Foo(i, ch)
}
count := 0
for count < 5 {
count += <-ch
}
}
通道属于复合类型,我们使用make函数创建通道,通道类型是int,也就是我们可以使用该通道传递int类型的值。我们在主函数中向Foo函数传递通道作为参数,当Foo函数执行结束后,通过通道发送数字1(ch <- 1),在主函数中我们进行计数,从通道中读取值(<- ch),当通道中没有值的时候,我们将阻塞等待。当五个协程都执行结束后退出循环。使用通道,我们还可以实现生产者和消费者模式。
package main
import (
"fmt"
"math/rand"
)
func Consumer(ch <-chan int, result chan<- int) {
sum := 0
for i := 0; i < 5; i++ {
sum += <-ch
}
result <- sum
}
func Producer(ch chan<- int) {
var num int
for i := 0; i < 5; i++ {
rand.Seed(20)
num = rand.Intn(100)
ch <- num
}
}
func main() {
ch := make(chan int)
result := make(chan int)
go Producer(ch)
go Consumer(ch, result)
fmt.Printf("result: %d\n", <-result)
}
我们在生产者和消费者之间使用ch通道传递数据,使用reslut通道给主函数返回结果。注意观察Consumer函数和Producer函数的参数列表,这里通道参数的传递略有不同,指明了通道的方向,chan<-代表我们可以向通道写数据,但是不能使用通道读数据,<-chan正好相反,只能从通道中读取数据而不可以写入数据。
可以看到,go语言实现并发非常简单,借用通道,又可以在不同的协程之间方便的传输数据。
有疑问加站长微信联系(非本文作者)