上一篇: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)
}
运行结果如下:
六、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)
}
运行结果如下:
七、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)
}
}
运行结果如下:
八、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
}
}
}
运行结果如下:
九、向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)
}
}
部分运行截图如下:
十、管道在默认情况下是双向的
// 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
}
}
}
运行结果:
十二、使用 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)
}
}
运行结果:
欢迎大家指正补充,感谢阅读。