实践三:协程与Channel多种应用场景整理

789 阅读3分钟

goroutine和channel让go 多并发显得简单而且效率很高,本节主要针对协程和管道在不同的应用场景下的具体实现。

1. channel与interface结合,完成多态

type A interface {
	Test()
}

type B struct{}
type C struct{}

func (b *B) Test() {
	fmt.Println("B...")
}

func (c *C) Test() {
	fmt.Println("C...")
}
func chanTest() {
	//chan 与interface结合,完成多态
	ch := make(chan A, 2)
	b := new(B)
	c := new(C)
	ch <- b
	ch <- c
	close(ch)
	for a := range ch {
		a.Test()
	}
}

输出结果如下:

B...
C...

Process finished with exit code 0

2. channel与func相结合

type A interface {
	Test()
}

type B struct{}
type C struct{}

func (b *B) Test() {
	fmt.Println("B...")
}

func (c *C) Test() {
	fmt.Println("C...")
}
func chanTest() {
	ch1 := make(chan func(), 2)
	b := new(B)
	c := new(C)
	ch1 <- b.Test
	ch1 <- c.Test

	close(ch1)
	for a := range ch1 {
		a()
	}
}

输出结果如下:

B...
C...

Process finished with exit code 0

3. channel传递数据的速度

测试1s通过管道完成多少数据的传递

func chanTest(){
    ch := pump() //无限获取chan中的数据

	go func() {
		for {
			if i, ok := <-ch; ok {
				fmt.Println(i)
			} else {
				fmt.Println("break...")
				break
			}
		}
	}()
	select {
	case <-time.After(time.Second):
		return
	}
}
func pump() chan int {
	ch := make(chan int)
	go func() { //goroutine运行不会因为方法pump的结束而结束,等待主程序结束而结束
		for i := 0; ; i++ {
			ch <- i
		}
	}()
	return ch
}

结果输出如下,完成了14万数据的传输:

4. 定时器和计时器

func chanTest(){
    //定时器和计时器
	tick := time.Tick(time.Second)
	boom := time.After(5 * time.Second)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		}
	}
}

输出结果如下:

tick.
tick.
tick.
tick.
tick.
BOOM!

Process finished with exit code 0

  • time.Afer():理解为计时器,等待一定时间,会向管道中发送信号,结束程序
  • time.Tick():理解为定时器,每隔一定时间,定时向管道发送信号,执行某一操作,可以用来万层Cron任务

5. 生成器

每一次调用生成一个偶数值


func NewInteger() chan int {
	ch := make(chan int)
	go func() {
		for i := 0; ; i++ {
			ch <- i * 2
		}
	}()
	return ch
}

func NextInteger(ch chan int) int {
	return <-ch
}

func generateTest() {
	next := NewInteger()
	for {
		fmt.Println(NextInteger(next))
		time.Sleep(time.Second)
	}
}

结果如下:

6. recover 完成try-catch操作

go语言中没有try-catch语句,但是可以通过recover来操作panic报错

func chanAndRecover() {
	ch := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			ch <- i
		}
	}()

	for i := 0; i < 5; i++ {
		go func(i int) {
			//如果不加recover来处理panic,一旦一个goroutine发生错误,会影响其他goroutine执行
			defer func() {
				if err := recover(); err != nil {  //catch操作
					log.Println("Error:", i)
				}
			}()
			fmt.Println(i, 10/i)
		}(<-ch)
	}

	time.Sleep(2 * time.Second)
}

结果输出:

1 10
3 3
2 5
4 2
2020/12/14 17:12:48 Error: 0

Process finished with exit code 0

7. Future

多种操作之间没有相互依赖关系,可以并行执行

func futuresTest(a, b int) {
	// old type,计算a1与b1之间没有任何关系,可以并行执行
	start := time.Now()
	a1 := oldCompute(a)
	b1 := oldCompute(b)
	allTime := time.Since(start).Nanoseconds()
	fmt.Printf("time=%d,result=%d\n", allTime, a1*b1)

	// new type
	start = time.Now()
	cha := newCompute(a)
	chb := newCompute(b)
	allTime = time.Since(start).Nanoseconds()
	fmt.Printf("time=%d,result=%d\n", allTime, <-cha*<-chb)
}

输出耗时如下:

time=2000859200,result=400000000
time=0,result=400000000

Process finished with exit code 0

8. 模拟连接池

参见GO第一次实践:GO实现PG数据库连接池

9. 控制访问次数

参见实践四:GO控制访问流量

10. 更多

后期再补充