一、问题
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.WaitGroupmu := sync.Mutex{} // 保护 results 共享资源for _, url := range urls {wg.Add(1)go func(url string) {defer wg.Done()result := wc(url)mu.Lock()//通过上锁,避免并发访问mapresults[url] = resultmu.Unlock()}(url)//把url当前的值传入并发线程中,而不是共用一个全局url}wg.Wait()return results
}