select+chan控制goroutine结束
1 一旦向chan中写入值了,select就执行之前阻塞的该通道,执行完后,再执行return,结束goroutine,
func main() { stop := make(chan bool) go func() { for { select { case <-stop: fmt.Println("监控退出,停止了...") return default: fmt.Println("goroutine监控中...") time.Sleep(2 * time.Second) } } }() time.Sleep(10 * time.Second) fmt.Println("可以了,通知监控停止") stop<- true //为了检测监控过是否停止,如果没有监控输出,就表示停止了 time.Sleep(5 * time.Second) }
注意select执行的时候是单线程,即如果只有case,要等每个case后的chan运行完,它才会去判断要执行那个case,所以尽量不要以自定义函数的形式返回chan,
package main import ( "context" "fmt" "sync" "time" ) var res = make(map[string]string) var rw sync.RWMutex func writeMap(key string) (chan int){ rw.Lock() res[key] = (key + " 网站爬虫结果") rw.Unlock() c := make(chan int, 1) c <- 0 time.Sleep(time.Second * 10) return c } func doSomething(ctx context.Context, key string) { select { case <-ctx.Done(): fmt.Println(key + "超时了,爬取未完成") case <- writeMap(key): fmt.Println(key + "网站爬取完成") } } func main() { var url = []string{ "www.baidu.com", "www.123.com", "www.456.com", } for _, num := range url { //ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*5) ctx, _ := context.WithTimeout(context.Background(), time.Second * 1) //defer cancel() go doSomething(ctx, num) } time.Sleep(time.Second * 5) fmt.Println(res) }
通过下面这个程序可以更清晰的看出来,
换句话说select后的case要就绪后才随机选一个,如果没有就绪就会先运行完,看看是不是阻塞、能不能写入或读出数据,如果都不能且没有defalut,就阻塞等待,否则执行default,
package main import ( "fmt" "time" ) func test() chan int { time.Sleep(time.Second * 5) res := make(chan int, 1) res <- 1 return res } func main() { c := make(chan int, 1) c <- 1 select { case <-c: fmt.Println("停顿5秒后才输出:aaa") case <-test(): fmt.Println("bbb") default: fmt.Println("不会直接走default") } }
利用cancel和定时器控制协程结束
https://blog.csdn.net/weixin_44879611/article/details/115537192?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.pc_relevant_paycolumn_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.pc_relevant_paycolumn_v2&utm_relevant_index=1