一、问题
1.1描述
我想要检测一组url的运行状态,如果ok则返回true,结果返回的结果是空的
func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)
for _, url := range urls {
go func() {
results[url] = wc(url)
}()
}
return results
}
1.2 分析
因为CheckWebsites
函数启动了多个 goroutine
,但没有等待它们完成。在 Go 中,goroutines
是并发执行的,但它们的完成时间是不确定的。如果主函数在所有 goroutines
完成之前返回结果,那么结果会不完整或不准确。
1.3 改进
使用sync.WaitGrou
p来确保主函数等待所有 goroutines
完成。sync.WaitGroup
提供了一种机制,用于等待一组操作完成。
import (
"sync"
)
func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)
var wg sync.WaitGroup//创建一个sync.WaitGroup实例:
mu := sync.Mutex{} // 保护 results 共享资源
for _, url := range urls {
wg.Add(1)
go func() {
defer wg.Done()//调用Done方法减少计数器
result := wc(url)
results[url] = result
}()
}
wg.Wait()//在主goroutine中,调用Wait方法阻塞,直到计数器变为0:
return results
}
二、新问题
问题一:在上面的代码中,共用一个url(类似于变成了全局变量),可能会出现:主线程运行结束,线程A-线程N还没有执行,那么url变量就变成了urls的最后一个数据。那么线程A-线程N所访问的全是url都相同。导致results的结果只有最后一个url才有效。
解决方法:把url传递到并发线程中
问题二:多线程下,并发访问了results;
解决方法:通过加锁解决。
func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)
var wg sync.WaitGroup
mu := sync.Mutex{} // 保护 results 共享资源
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
result := wc(url)
mu.Lock()//通过上锁,避免并发访问map
results[url] = result
mu.Unlock()
}(url)//把url当前的值传入并发线程中,而不是共用一个全局url
}
wg.Wait()
return results
}