Bootstrap

go 并发

一、问题

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.WaitGroup来确保主函数等待所有 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
}
;