上一篇:golang学习(二十):goroutine与channel基本使用

五、channel 的基本介绍
//channel的数据放满后,就不能放入了
//如果从channel取出数据后,又可以继续放入
//可以这种方式取出数据  <-intChan
//channel的基本使用
//创建一个allChan,最多可以存放10个任意数据类型变量
package main

import "fmt"

func main() {

	var allChan chan interface{}
	allChan = make(chan interface{}, 10)
	type Cat struct {
		Name string
		Age int
	}
	allChan <- 10
	allChan <- "allen"
	cat1 := Cat{Name: "tom", Age: 18,}
	allChan <- cat1
	//取出
	<- allChan
	<- allChan
	newcat := <- allChan
	fmt.Printf("newcat=%T, newcat=%v\n", newcat, newcat)
	//类型断言
	a := newcat.(Cat)
	//错误的写法
	//fmt.Printf("newcat.Name=%v", newcat.Name)
	fmt.Printf("newcat.Name=%v", a.Name)
}

运行结果如下:

golang学习(二十一):goroutine与channel(二)

 

六、channel的关闭
//使用内置函数close关闭channel,关闭后只能读不能写数据
//六、channel的关闭
//使用内置函数close关闭channel,关闭后只能读不能写数据

package main

import "fmt"

func main()  {

	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	//关闭通道
	close(intChan)
	//不能再写数据
	//intChan <- 300
	//可以读数据
	v1 := <- intChan
	fmt.Printf("v1=%v",v1)
}

运行结果如下:

 golang学习(二十一):goroutine与channel(二)

 

七、channel的遍历
//channel支持for-range方式进行遍历
//注意
//在遍历时,如果channel没有关闭,会出现deadlock错误
//七、channel的遍历
//channel支持for-range方式进行遍历
//注意
//在遍历时,如果channel没有关闭,会出现deadlock错误

package main

import "fmt"

func main() {
	intChan2 := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan2 <- i
	}
	//注意关闭管道
	close(intChan2)
	//遍历
	//遍历channel时不能使用普通for循环
	for v := range intChan2 {
		fmt.Printf("v=%v\n",v)
	}
}

 运行结果如下:

 golang学习(二十一):goroutine与channel(二)

 

八、channel和goroutine结合
//八、channel和goroutine结合

package main

import (
	"fmt"
	"time"
)

//写数据
func writedata(intChan chan int) {
	for i := 1; i <= 50; i++{
		intChan <- i
		fmt.Printf("写入数据=%v\n",i)
		//time.Sleep(time.Second * 1)
	}
	close(intChan)
}
//读数据
func readdata(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <- intChan
		if !ok {
			break
		}
		time.Sleep(time.Second )
		fmt.Printf("读取数据=%v\n",v)
	}
	//读取数据后,即完成任务
	exitChan <- true
	close(exitChan)
}

func main() {
	//创建两个管道
	intChan := make(chan int, 10)
	exitChan := make(chan bool, 1)
	go writedata(intChan)
	go readdata(intChan,exitChan)

	//time.Sleep(time.Second * 10)
	for {
		_, ok := <- exitChan
		if !ok {
			break
		}
	}
}

 运行结果如下:

golang学习(二十一):goroutine与channel(二)

 

九、向intChan放入1-80000个数

 

//九、向intChan放入1-80000个数

package main

import (
	"fmt"
	"time"
)

func putNum(intChan chan int) {
	for i := 1; i <= 80000; i++{
		intChan <- i
	}

	//关闭通道
	close(intChan)
}

//
func primeNum(intChan chan int, primeChan chan int,exitChan chan bool) {
	//使用for循环
	//var num int
	var flag bool
	for {
		//time.Sleep(time.Millisecond * 10)
		num, ok := <- intChan
		if !ok{ //管道取不到东西
			break
		}
		flag = true
		//判断num是否素数
		for i := 2; i < num; i++{
			if num % i == 0 {
				flag = false
				break
			}
		}

		if flag {
			//将这个数放入到primChan
			primeChan <- num
		}
	}
	fmt.Println("有一个协程取不到数据,退出")
	//向exitChan写如true
	exitChan <- true

}

func main() {

	intChan := make(chan int, 1000)
	primeChan := make(chan int, 8000)
	//标识退出的管道
	exitChan := make(chan bool, 4)

	//计算时间
	start := time.Now().Unix()

	//开启一个协程向intChan放入1-8000
	go putNum(intChan)
	//开启4个协程,从intChan取出数据,并判断是否为素数,如果是,就放入到primChan
	for i := 0; i < 4; i++{
		go primeNum(intChan, primeChan, exitChan)
	}

	//主线程进行处理
	//
	go func() {
	for i := 0; i < 4; i++ {
		<- exitChan
	}

	//统计时间
	end := time.Now().Unix()
	fmt.Println("使用协程耗费的时间=",end - start)

	//当我们从exitChan取出4个结果,就可以关闭primeNum
	close(primeChan)
}()

	//遍历primeNum,把结果取出
	for {
		res, ok := <- primeChan
		if !ok {
			break
		}
		//将结果输出
		fmt.Printf("素数=%d\n",res)
	}
}

部分运行截图如下:

golang学习(二十一):goroutine与channel(二)

 

十、管道在默认情况下是双向的
// var chan1 chan int
//管道可以声明为只读或者可写
//十、管道在默认情况下是双向的
// var chan1 chan int
//管道可以声明为只读或者可写

package main

import "fmt"

func main() {
	//1. 声明为只写
	var chan2 chan <- int
	chan2 = make(chan int, 3)
	chan2 <- 20
	//num := <- chan2
	fmt.Println(chan2)

	//2. 声明为只读
	var chan3 <- chan int
	num2 := <- chan3
	fmt.Println(num2)
}

 

十一、使用select可以解决从管道取数据的阻塞问题
//十一、使用select可以解决从管道取数据的阻塞问题

package main

import "fmt"

func main() {
	//定义一个管道 10个数据int
	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan <- i
	}
	//定义一个管道   5个数据string
	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan <- "hello" + fmt.Sprintf("%d", i)
	}

	//传统方法在遍历管道时,如果不关闭会因为阻塞导致deadlock
	//如果我们不缺定在什么时候关闭该管道,可以使用select方式解决

	for {
		select {
		//这里如果intChan一直没有关闭,不会一直阻塞
		//会自动到下一个case匹配
		case v := <-intChan:
			fmt.Printf("从intChan读取的数据为%d\n", v)
			//time.Sleep(time.Second * 1)
		case v := <-stringChan:
			fmt.Printf("从stringChan读取的数据为%s\n", v)
			//time.Sleep(time.Second * 1)
		default:
			fmt.Printf("都取不到\n")
			return
		}
	}
}

 运行结果:

golang学习(二十一):goroutine与channel(二)

 

十二、使用 defer + recover 捕获抛出的panic
//这种机制即使这个协程发生问题,主线程不受影响,可以继续执行
//这种机制即使这个协程发生问题,主线程不受影响,可以继续执行

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	for i := 0; i < 5; i++ {
		time.Sleep(time.Second * 1)
		fmt.Println("hello,world")
	}
}

func test() {
	//这里我们可以使用defer + recover
	defer func() {
		//捕获test抛出的panic
		if err := recover(); err != nil {
			fmt.Println("test()发生错误", err)
		}
	}()
	//定义一个map
	var myMap map[int]string
	myMap[0] = "golang"
}
func main() {
	go sayHello()
	go test()
	for i := 0; i < 5; i++ {
		fmt.Println("main() ok=", i)
		time.Sleep(time.Second * 1)
	}
}

运行结果:

golang学习(二十一):goroutine与channel(二)

 

参考:尚硅谷韩顺平Go语言核心编程

 


欢迎大家指正补充,感谢阅读。

相关文章: