1 goroutine退出
1.1 超时退出
func main() {
var wg sync.WaitGroup
quit := time.Tick(time.Second * 2) // 给个定时器
wg.Add(1)
go func() {
defer wg.Done()
<-quit
fmt.Println("over goroutine")
return
}()
wg.Wait()
fmt.Println("over all")
}
2 context 通知退出
func main() {
var wg sync.WaitGroup
ctx, 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.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
fmt.Println("over")
return
}()
wg.Wait()
}
通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, 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 main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, 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 main
import (
"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 main
import (
"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 <- 0
close(ch)
}
func main() {
ch := make(chan int)
go say2("world", ch)
say("hello")
fmt.Println(<-ch)
}
使用channel将异步变为同步
**
package main
import (
"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) {
<-ch
fmt.Println(i)
}
主协程如何等其余协程完再操作
sync包和channel来解决协程同步和通讯。
package main
import (
"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 main
import (
"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 main
import (
"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 main
import (
"fmt"
)
func main() {
chan1 := make(chan struct{}, 1)
chan2 := make(chan struct{}, 1)
chan_final := make(chan struct{}, 1)
chan1 <- struct{}{}
N := 100
go func(N int) {
for i := 1; i <= N; i += 2 {
<-chan1
fmt.Println(i)
chan2 <- struct{}{}
}
}(N)
go func(N int) {
for i := 2; i <= N; i += 2 {
<-chan2
fmt.Println(i)
if i == N {
chan_final <- struct{}{}
}
chan1 <- struct{}{}
}
}(N)
<-chan_final
}
dog cat fish 按这个输出一百句
package main
import (
"fmt"
"sync"
)
//dog cat fish 按这个输出一百句 第一题
var wg sync.WaitGroup
func main() {
// counter use to count the output
var counter int = 1000
dogch := 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 := 0
for dogtimes < counter {
select {
case <-dogch:
fmt.Println("dog")
dogtimes++
// let cat run
catch <- struct{}{}
}
}
wg.Done()
}
func cat(catch chan struct{}, counter int, fishch chan struct{}) {
cattimes := 0
for cattimes < counter {
select {
case <-catch:
fmt.Println("cat")
cattimes++
//let fish run
fishch <- struct{}{}
}
}
wg.Done()
}
func fish(fishch chan struct{}, counter int, dogch chan struct{}) {
fishtimes := 0
for fishtimes < counter {
select {
case <-fishch:
fmt.Println("fish")
fishtimes++
// let dog run
dogch <- 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 channel
case <-done: // If receive signal on done channel - Return
return
default:
}
}
}()
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 return
return
}()
<-done // Exit Main when Terminate Signal received
}
笔试题参考
https://www.zhihu.com/column/interview 以及这个人的知乎其他文章