浅谈Go语言并发机制
Go语言并发简介
并发是指同时执行多个任务的能力。Go语言内置了并发编程的支持,可以非常方便地编写高并发程序。
Go语言的并发模型依赖于go函数和channel这两个基本元素。
Go函数
在Go语言中,我们可以用go
关键字来启动一个goroutine(轻量级线程),goroutine的调度由Go语言运行时完成。
以下是一个启动goroutine的简单示例:
package main
import (
"fmt"
"time"
)
func main() {
go func() {
for i := 1; i <= 5; i++ {
fmt.Println("goroutine: ", i)
time.Sleep(time.Second)
}
}()
for i := 1; i <= 5; i++ {
fmt.Println("main: ", i)
time.Sleep(time.Second)
}
}
运行结果如下:
main: 1
goroutine: 1
main: 2
goroutine: 2
main: 3
goroutine: 3
main: 4
goroutine: 4
main: 5
goroutine: 5
这里我们启动了一个匿名函数,使用go
关键字创建了一个新的goroutine。在主函数中,我们也打印了5个消息。
可以发现,主函数和goroutine之间并没有严格的交替执行,而是由Go语言运行时随机调度的。
Channel
在Go语言中,我们可以使用channel来进行协程间通信。
channel是Go语言内置的双向链表,支持并发的读写操作。channel类似于Unix中的管道,可以用于协程之间的同步和通信。
以下是一个channel的简单示例:
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 1; i <= 5; i++ {
c <- i
}
close(c)
}()
for v := range c {
fmt.Println(v)
}
}
运行结果如下:
1
2
3
4
5
这里我们创建了一个大小为1的channel,用于在goroutine和主函数之间通信。在匿名函数中,我们通过c <- i
向通道中写入数据,最后通过close(c)
关闭通道。在主函数中,我们使用for...range
语句从通道中读取数据,并打印输出。
Go语言并发机制
Go语言并发机制通过goroutine和channel来实现,并发模型简单且易于使用,适合处理高并发场景。
以下是一个并发模型的简单示例:
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 1; i <= 5; i++ {
c <- i
}
close(c)
}()
for v := range c {
go func(v int) {
fmt.Println("goroutine: ", v)
}(v)
}
}
运行结果如下:
goroutine: 3
goroutine: 4
goroutine: 5
goroutine: 1
goroutine: 2
这里我们先创建了一个通道,然后在一个goroutine中向通道中写入数据,最后关闭通道。在主函数中,我们使用for...range
语句从通道中读取数据,并使用go
关键字启动一个新的goroutine打印消息。
从运行结果可以看出,启动的五个goroutine并不是严格按照顺序依次执行,而是由Go语言运行时随机调度的。
另外,由于我们使用了go func(v int){fmt.Println("goroutine: ", v)}(v)
的形式来启动goroutine,所以每个goroutine都是在新的线程中执行,相互之间并不会互相影响。而如果不使用go
关键字来启动goroutine,则当前的goroutine就会阻塞等待消息被读取,从而无法并发执行其他的任务。
例子说明
goroutine并发下载文件
在多数情况下,我们下载很多小文件的时候,单线程下载速度太慢,很容易使用户等待。使用goroutine则可以简单地达到并发下载的目的。
以下是一个使用goroutine并发下载文件的简单示例:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func downloadFile(url string, fileName string) {
resp, err := http.Get(url)
defer resp.Body.Close()
if err != nil {
fmt.Println("Error: ", err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error: ", err)
return
}
err = ioutil.WriteFile(fileName, body, 0644)
if err != nil {
fmt.Println("Error: ", err)
return
}
fmt.Println("Downloaded: ", url)
}
func main() {
c := make(chan int)
files := []string{
"https://golang.google.cn/doc/gopher/bumper.png",
"https://golang.google.cn/pkg/http/",
"https://golang.google.cn/pkg/io/",
}
for _, url := range files {
go func(url string) {
fileName := url[strings.LastIndex(url, "/")+1:]
downloadFile(url, fileName)
c <- 1
}(url)
}
for i := 0; i < len(files); i++ {
<-c
}
}
运行结果如下:
Downloaded: https://golang.google.cn/pkg/io/
Downloaded: https://golang.google.cn/doc/gopher/bumper.png
Downloaded: https://golang.google.cn/pkg/http/
这里我们定义了downloadFile
函数用于下载文件,然后在主函数中使用for
循环启动多个goroutine并发执行下载任务。我们使用通道来同步各个goroutine执行的任务。
从运行结果中可以看出,这个程序可以同时下载多个文件并且可以很快地完成所有任务。
超时请求
在网络请求中,经常需要设置超时时间,以防止请求长时间未响应而阻塞应用程序。使用goroutine可以方便地实现超时请求的功能。
以下是一个使用goroutine实现超时请求的简单示例:
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
c := make(chan error)
url := "https://golang.google.cn/pkg/io/"
go func() {
resp, err := http.Get(url)
if err != nil {
c <- err
} else {
defer resp.Body.Close()
c <- nil
}
}()
select {
case err := <-c:
if err != nil {
fmt.Println("Error: ", err)
} else {
fmt.Println("Request OK")
}
case <-time.After(3 * time.Second):
fmt.Println("Request timed out")
}
}
运行结果如下:
Request OK
这里我们创建了一个通道和一个goroutine,同时使用了select
和time.After
函数来模拟超时。当请求超时或者执行完成后,程序可以自动终止。如果在规定时间内请求没有得到响应,则打印提示信息。
结论
Go语言的并发机制基于goroutine和channel实现,简单易用,适合处理高并发场景。在实际应用中,我们可以通过使用goroutine来实现并发执行任务,使用channel来进行不同goroutine之间的协作和通信,从而实现高效的并发编程。
如果我们在合适的场景下使用goroutine和channel,可以显著地提高程序的性能和吞吐量,从而更好地满足实际需求。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Go语言并发机制 - Python技术站