1 用time.After和context实现,两个方法本质上都是用了context或chan进行传递信息,如果go协程结束了,则用cancel关闭协程或往channel中传入值,同时用case time.After进行阻塞,若go协程超时了,则会走case time.After通道,
两种方法,第一种是go协程里嵌套了go协程,第二种是先启动n个go协程去运行,再启动n个go协程去监控,

package main import ( "context" "fmt" "sync" "time" ) //实现从三个网站爬取数据并用map保存的功能,如果协程超过1秒,则直接返回, // 1 用time.After结合context实现, // https://shockerli.net/post/golang-select-time-implement-timeout/ var res = make(map[string]string) var rw sync.RWMutex func writeMap(ctx context.Context, key string, cancel context.CancelFunc) { rw.Lock() res[key] = (" " + key + " 网站爬取结果") rw.Unlock() // 写这个是为了节省时间,因为有可能go协程的运行时间小于WithTimeout中所给的时间, // 导致程序已经取完数据了,go协程仍然没有结束, cancel() } // 这里必须要开启两个go协程,一个用于运行爬虫程序,另一个用于监控时间,其中一个case是time.After即超时的channel, // 另一个是go协程结束后,会close ctx.Done,或者在规定的WithTimeout时间内go协程没有结束的话,WithTimeout内部会close ctx.Done func doSomething(key string) { ctx, cancel := context.WithTimeout(context.Background(), time.Second * 4) // 这个是为了防止忘记关闭协程,导致内存泄漏, defer cancel() go writeMap(ctx, key, cancel) select { case <- ctx.Done(): fmt.Println(key + "网站爬取完毕!!!") // 注意这个time.After返回的是一个time类型的chan,所以这里可以这样写, case <-time.After(time.Second * 5): fmt.Println(key + "网站爬取超时!!!") } } func main() { var url = []string{ "www.baidu.com", "www.123.com", "www.456.com", } for _, num := range url { go doSomething(num) } time.Sleep(time.Second * 8) fmt.Println(res) }
用time.After和chan实现

// 只用time.After实现,这个方法的关键是当goroutine结束时,向chan通道中传入一个string,之后再用一个case去读取, // 如果能读到,则说明没有超时,否则走超时的case, var res sync.Map var wg sync.WaitGroup // 需要缓冲,不然阻塞 var sign = make(chan string, 3) func writeMap(key string) { res.Store(key, key+" 网站爬虫结果") } func doSomething(key string) { writeMap(key) sign <- key } func doSomething1(key string) { for { writeMap(key) } sign <- key } func main() { var url = []string{ "www.baidu.com", "www.123.com", "www.456.com", } for index, num := range url { if index == 2 { go doSomething1(num) } else { // 用死循环查看超时的情况, go doSomething(num) } } wg.Add(len(url)) for _, num := range url { go func() { defer wg.Done() select { case r := <-sign: fmt.Println(r + "网站爬取完毕!!!") return // 注意这个time.After返回的是一个time类型的chan,所以这里可以这样写, case <-time.After(time.Second * 3): fmt.Println(num + "网站爬取超时!!!") return } }() } wg.Wait() }
参考:https://shockerli.net/post/golang-select-time-implement-timeout/ 这是类似第二种方法,只不过只有一个goroutine,