1 goroutine退出
1.1 超时退出
func main() {var wg sync.WaitGroupquit := time.Tick(time.Second * 2) // 给个定时器wg.Add(1)go func() {defer wg.Done()<-quitfmt.Println("over goroutine")return}()wg.Wait()fmt.Println("over all")}
2 context 通知退出
func main() {var wg sync.WaitGroupctx, cancel := context.WithTimeout(context.Background(), time.Second*2)defer cancel()wg.Add(1)go func() {defer wg.Done()<-ctx.Done()fmt.Println("over goroutine")return}()wg.Wait()fmt.Println("over all")}
1. 2 外部被动退出:
func main() {var wg sync.WaitGroupwg.Add(1)go func() {defer wg.Done()ch := make(chan os.Signal)signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)<-chfmt.Println("over")return}()wg.Wait()}
通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:
package mainimport "fmt"func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 把 sum 发送到通道 c}func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int)go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)x, y := <-c, <-c // 从通道 c 中接收fmt.Println(x, y, x+y)}
使用channel 计算fibonacci数
package mainimport ("fmt")func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)}func main() {c := make(chan int, 10)go fibonacci(cap(c), c)// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不// 会结束,从而在接收第 11 个数据的时候就阻塞了。for i := range c {fmt.Println(i)}}
借助channel完成消息的完整传递
package mainimport ("fmt""time")func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s, (i+1)*100)}}func say2(s string) {for i := 0; i < 5; i++ {time.Sleep(150 * time.Millisecond)fmt.Println(s, (i+1)*150)}}func main() {go say2("world")say("hello")}
say2 只执行了 3 次,而不是设想的 5 次
在 goroutine 还没来得及跑完 5 次的时候,主函数已经退出了。
改进
引入一个信道,默认的,信道的存消息和取消息都是阻塞的,在 goroutine 中执行完成后给信道一个值 0,则主函数会一直等待信道中的值,一旦信道有值,主函数才会结束。
package mainimport ("fmt""time")func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s, (i+1)*100)}}func say2(s string, ch chan int) {for i := 0; i < 5; i++ {time.Sleep(150 * time.Millisecond)fmt.Println(s, (i+1)*150)}ch <- 0close(ch)}func main() {ch := make(chan int)go say2("world", ch)say("hello")fmt.Println(<-ch)}
使用channel将异步变为同步
**
package mainimport ("fmt""time")var ch = make(chan int)func main() {for i := 1; i <= 75; i++ {go print(i)ch <- 0}time.Sleep(5 * time.Second) //wait all goroutines}func print(i int) {<-chfmt.Println(i)}
主协程如何等其余协程完再操作
sync包和channel来解决协程同步和通讯。
package mainimport ("fmt""sync""time")var wg sync.WaitGroup //定义一个同步等待的组func task(i int){fmt.Println("task...",i)//耗时操作任务,网络请求,读取文件time.Sleep(time.Second)wg.Done() //减去一个计数}func main(){for i:= 0;i<10;i++{wg.Add(1) //添加一个计数go task(i)}wg.Wait() //阻塞直到所有任务完成fmt.Println("over")}
利用缓冲信道channel协程之间通讯,其阻塞等待功能实现等待一组协程结束,不能保证其goroutine按照顺序执行
package mainimport ("fmt")var ch = make(chan int,10)func task(i int){fmt.Println("task...",i)ch <- i}func main(){for i:= 0;i<10;i++{go task(i)}for i:= 0;i<10;i++{<- ch}fmt.Println("over")}
利用无缓冲的信道channel协程之间通讯,其阻塞等待功能实现等待一组协程结束,保证了其goroutine按照顺序执行
package mainimport ("fmt""time")var ch = make(chan int)func task(i int){fmt.Println("task...",i)time.Sleep(time.Second)<- ch}func main(){for i:= 0;i<10;i++{go task(i)ch <- i}fmt.Println("over")}
golang两个协程交替打印1-100的奇数偶数
package mainimport ("fmt")func main() {chan1 := make(chan struct{}, 1)chan2 := make(chan struct{}, 1)chan_final := make(chan struct{}, 1)chan1 <- struct{}{}N := 100go func(N int) {for i := 1; i <= N; i += 2 {<-chan1fmt.Println(i)chan2 <- struct{}{}}}(N)go func(N int) {for i := 2; i <= N; i += 2 {<-chan2fmt.Println(i)if i == N {chan_final <- struct{}{}}chan1 <- struct{}{}}}(N)<-chan_final}
dog cat fish 按这个输出一百句
package mainimport ("fmt""sync")//dog cat fish 按这个输出一百句 第一题var wg sync.WaitGroupfunc main() {// counter use to count the outputvar counter int = 1000dogch := make(chan struct{}, 1)catch := make(chan struct{}, 1)fishch := make(chan struct{}, 1)wg.Add(3)// let run the three goroutine// the origin of the goroutine to set dogch <- struct {}{}dogch <- struct{}{}go dog(dogch, counter, catch)go cat(catch, counter, fishch)go fish(fishch, counter, dogch)wg.Wait()}func dog(dogch chan struct{}, counter int, catch chan struct{}) {dogtimes := 0for dogtimes < counter {select {case <-dogch:fmt.Println("dog")dogtimes++// let cat runcatch <- struct{}{}}}wg.Done()}func cat(catch chan struct{}, counter int, fishch chan struct{}) {cattimes := 0for cattimes < counter {select {case <-catch:fmt.Println("cat")cattimes++//let fish runfishch <- struct{}{}}}wg.Done()}func fish(fishch chan struct{}, counter int, dogch chan struct{}) {fishtimes := 0for fishtimes < counter {select {case <-fishch:fmt.Println("fish")fishtimes++// let dog rundogch <- struct{}{}}}wg.Done()}//题解:// 分别使用三个goroutine代表dog cat fish,由于携程执行的无顺序性,但是题目要求顺序循环输出,// 所以,我们使用通道来实现阻塞,通过传值到通道中来实现顺序执行// 首先定义了三个有缓冲通道 ,并设置了循环的次数counter// counter use to count the output// var counter int = 3// dogch := make(chan struct{}, 1)// catch := make(chan struct{}, 1)// fishch := make(chan struct{}, 1)// 然后对于每一个goroutine来说设置一个 for循环来执行counter次,里面采用for-select-case// 如果该通道没值 则会阻塞在这个地方直到有值传进来。// // the origin of the goroutine to set dogch <- struct {}{}// dogch <- struct{}{} 这一句话是三个goroutin启动的触发条件。// 第一个goroutine有值之后,执行第一个输出 dog,然后向catch里面传值,这个时候被阻塞的第一个goroutine//// 得以激活,一次类推,直到把循环走完,每个goroutine都done了,程序结束
一个goroutine生产消息,一个消费消息
func main() {ch := make(chan int)done := make(chan bool)go func() {for {select {case ch <- rand.Intn(5): // Create and send random number into channelcase <-done: // If receive signal on done channel - Returnreturndefault:}}}()go func() {for i := 0; i < 5; i++ {fmt.Println("Rand Number = ", <-ch) // Print number received on standard output}done <- true // Send Terminate Signal and returnreturn}()<-done // Exit Main when Terminate Signal received}
笔试题参考
https://www.zhihu.com/column/interview 以及这个人的知乎其他文章
