Golang并发编程优化指南:如何加速程序运行
Golang并发编程优化指南:如何加速程序运行 你好,
我正在开发一个程序,它可能会进行大量的网络调用,我认为这可能会严重拖慢程序速度,甚至可能导致更糟的问题。最让我担心的代码是下面这段:
for hostbuff.Scan() { //Buffer From File With Hostnames
assetips, err := net.LookupIP(hostbuff.Text()) //Lookup IP And Put in Array
// go assetips, err := net.LookupIP(hostbuff.Text()) //Would this work?
if err != nil {
fmt.Println("We had IP lookup error")
}
for _, ip := range assetips {
//do stuff
我使用 net.LookupIP 来解析大量主机名以获取 IP 地址,并将它们放入一个数组中。然后,我会对这些 IP 地址执行不同类型的工作。我最担心的是 net.LookupIP 函数。是否有推荐的方法或思路来使其并发执行,例如使用 Goroutine 或其他方法?我首先想到的是直接写成 go assetips, err := net.LookupIP(hostbuff.Text()),但我担心这可能会引发问题。
提前感谢。
更多关于Golang并发编程优化指南:如何加速程序运行的实战教程也可以访问 https://www.itying.com/category-94-b0.html
好的,这里有几件事需要提一下,因为这是一个有趣的话题。基本上,只有当你在每个goroutine中启动所有调用,并且每个goroutine都有一个无限循环来执行调用并定期将数据收集到某个地方时,并发执行一些网络调用才值得。否则,如果网络调用太短,在goroutine中启动可能产生与串行执行相同的效果,因为可能会在其它goroutine开始之前就结束了第一个调用。
假设你处于第一种情况,最好要知道操作系统允许的文件描述符数量是有限的(例如,在Linux上是65535),如果你想进行超过此限制的更多网络调用,就必须增加这个限制。了解处理文件描述符、带宽和TCP僵尸连接是相当困难的事情,这是有好处的。
另一点是你收集数据的地方,简单且廉价的方法是使用映射(maps),但你必须注意锁的问题。
了解了这些之后,我认为你可以做到,但需要权衡在你的特定情况下,让事情并发执行是否值得。
更多关于Golang并发编程优化指南:如何加速程序运行的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
要加速这段代码,可以使用 Goroutine 和 Channel 实现并发 DNS 解析。以下是优化后的示例:
package main
import (
"bufio"
"fmt"
"net"
"sync"
)
func main() {
// 假设 hostbuff 是从文件读取的 bufio.Scanner
var hostbuff *bufio.Scanner
// 创建带缓冲的 channel 用于传递结果
resultChan := make(chan struct {
host string
ips []net.IP
err error
}, 100)
// 创建 WaitGroup 等待所有 Goroutine 完成
var wg sync.WaitGroup
// 启动结果收集 Goroutine
go func() {
wg.Wait()
close(resultChan)
}()
// 并发处理每个主机名
for hostbuff.Scan() {
host := hostbuff.Text()
wg.Add(1)
go func(h string) {
defer wg.Done()
ips, err := net.LookupIP(h)
resultChan <- struct {
host string
ips []net.IP
err error
}{h, ips, err}
}(host)
}
// 处理结果
for result := range resultChan {
if result.err != nil {
fmt.Printf("Host %s: IP lookup error: %v\n", result.host, result.err)
continue
}
for _, ip := range result.ips {
// 对每个 IP 执行后续操作
fmt.Printf("Host %s: IP %s\n", result.host, ip.String())
// do stuff with ip
}
}
}
对于更精细的控制,可以使用 worker pool 模式限制并发数量:
func main() {
var hostbuff *bufio.Scanner
// 设置最大并发数
maxWorkers := 50
jobs := make(chan string, 100)
results := make(chan struct {
host string
ips []net.IP
err error
}, 100)
// 启动 worker pool
var wg sync.WaitGroup
for i := 0; i < maxWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for host := range jobs {
ips, err := net.LookupIP(host)
results <- struct {
host string
ips []net.IP
err error
}{host, ips, err}
}
}()
}
// 发送任务
go func() {
for hostbuff.Scan() {
jobs <- hostbuff.Text()
}
close(jobs)
}()
// 收集结果
go func() {
wg.Wait()
close(results)
}()
// 处理结果
for result := range results {
if result.err != nil {
fmt.Printf("Host %s: IP lookup error: %v\n", result.host, result.err)
continue
}
for _, ip := range result.ips {
// do stuff with ip
fmt.Printf("Host %s: IP %s\n", result.host, ip.String())
}
}
}
关键优化点:
- 使用 Goroutine 并发执行
net.LookupIP调用 - 通过 Channel 安全地传递结果
- 使用
sync.WaitGroup确保所有 Goroutine 完成 - Worker pool 模式可以控制最大并发数,避免系统资源耗尽
直接使用 go assetips, err := net.LookupIP(hostbuff.Text()) 的问题在于:
- 无法获取返回值
- 无法控制并发数量
- 可能导致 Goroutine 泄露
- 错误处理困难

