详解Golang 中的并发限制与超时控制
前言
该文主要讲述在 Golang 中如何控制并发数以及如何实现请求的超时控制。在实际的开发中,这两个问题是非常重要的,同时在一些性能优化场景下也会起到很大的作用。
控制并发
在 Golang 中,我们可以通过设置goroutine
的数量来控制并发的数量。假设我们有一个需求,在获取照片的时候我们不希望并发数量过高,同时也不希望请求照片的过程中出现错误。我们此时可以通过使用一个带有限制并发量的队列来实现这个功能。
基本思路
我们可以先定义一个函数来获取照片,假设这个函数是 getPicture
,然后我们创建一个带有固定缓存大小的 Channel,将需要获取照片的任务放进去。我们可以定义一个goroutine
,并从 Channel 中取出任务。对于每个任务,我们可以将此任务的处理放入到另外一个带有一个goroutine
的 Channel 中,并阻塞,等待网上获取照片,同时会使用一个计数器来记录处理的任务的数量。每当处理完一个任务时,该计数器的值会自增,最终在该 Channel 中收到所有的响应之后,我们可以将所有任务的响应放入到一个结果数组中。
示例代码
下面是一个带有并发限制的获取照片的示例代码,只会同时进行2个任务的处理。
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
// 声明并发限制为 2 的缓存型通道
c := make(chan int, 2)
var urls = []string{"http://www.baidu.com", "http://www.sina.com", "http://www.sohu.com", "http://www.qq.com", "http://www.taobao.com", "http://www.tmall.com", "http://www.jd.com", "http://www.163.com"}
responses := []string{}
// 开始处理任务
for _, url := range urls {
// 将任务放到 Channel 中,并开启 goroutine 进行处理
c <- 1
go func(url string) {
// 处理任务
response := fmt.Sprintf("Url: %s, Result: XXX", url)
responses = append(responses, response)
// 执行完任务后,从 Channel 中取出标记
<-c
}(url)
}
// 等待任务完成
for {
if len(c) == 0 {
break
}
}
// 处理所有任务响应,仅为示例,执行一个简单的输出
for _, response := range responses {
fmt.Println(response)
}
fmt.Printf("Elapsed time: %s", time.Since(start))
}
超时控制
在 Golang 中,我们也可以通过一个 context
变量和 time
包配合来实现请求超时的控制。下面我们将通过一个示例来讲解如何实现该功能。
基本思路
我们假设我们要请求一个 API 以获取数据,但是由于网络可能出现问题或 API 服务不可用等情况,我们不能保证所有请求都能及时得到响应。另外,我们也不希望等待所有请求都超时后才能返回结果。因此,我们可以使用上下文和超时作为 API 请求的两个主要参数。我们可以使用context
模块来控制请求的生命周期,而 time.AfterFunc()
可以用于设置超时定时器,一旦定时器被触发时,将会导致 API 请求取消。
示例代码
下面是一个带有超时控制的 API 请求示例。
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func main() {
// 创建上下文
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
tr := &http.Transport{}
client := &http.Client{Transport: tr}
// 发送请求
req, _ := http.NewRequest("GET", "http://httpbin.org/get", nil)
req = req.WithContext(ctx)
// 设置超时定时器
ch := make(chan int, 1)
go func() {
time.Sleep(3 * time.Second)
ch <- 1
}()
// 等待 API 请求响应
select {
case <-ch:
fmt.Println("API请求超时")
case <-ctx.Done():
fmt.Println("API请求取消")
return
default:
res, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
defer res.Body.Close()
fmt.Println("API请求成功")
}
}
在上面的示例中,我们首先创建一个上下文变量,然后使用一个 http.Client
对象初始化一个 GET 请求。接着我们创建一个带有指定超时时间的 Channel,并使用定时器开启一个子线程等待超时时间到达。最后,我们在一个 select
语句中等待 API 请求响应返回,一旦定时器到期,我们将会收到一个来自超时定时器的响应,根据响应来取消 API 请求。
结尾
本篇文章主要讲解了 Golang 中如何控制并发以及如何实现请求的超时控制。这些方案都是非常重要的,可以帮助我们更好地定制化和优化在大型系统中的性能。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Golang 中的并发限制与超时控制 - Python技术站