main.go
// Go语言·协程Goroutine·管道Channel
package main
import (
// model "day32/model"
"fmt"
"time"
)
var content string = `
————————————————Go语言·协程Goroutine·管道Channel————————————————————
一、协程&管道双剑合璧
1.死锁,管道阻塞
编译器,发现一个管道只有写,没有读,则管道,会阻塞
不停的写入管道,却没有从管道中读取。
1.写的地方报错:deadlock
2.读的地方报错:deadlock
写管道和读管道的频率不一致,无所谓,有效阻塞。
二、求素数(1-300000)
思路分析:
model.PrimeCount()
素数:299993
主线程退出~~~完成!耗时: 4 秒
三、使用细节和注意事项
1.管道可以声明只读或只写
// 只写
var chan2 chan<- int
// 只读
var chan3 <-chan int
应用场景:
协程参数只读只写
协程参数,可以声明为只写,只读
2.在默认情况下,管道是双向的。
3.select可以解决从管道取数据的阻塞问题
select {
case v:=<-intChan:
fmt.Println("intChan数据:",v)
time.Sleep(time.Second)
case v:=<-stringChan:
fmt.Println("stringChan数据:",v)
time.Sleep(time.Second)
default:
fmt.Println("什么也么有!!")
time.Sleep(time.Second)
return
}
4.协程中使用recover,解决协程中出现panic,解决程序崩溃
// defer +recover
defer func() {
if err:=recover();err != nil {
// 记录日志,发消息通知管理员等操作。
fmt.Println("程序报错咯:",err)
}
}()
`
func main() {
go sayHello()
go testMap()
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println("main() :OK !")
}
}
func test() {
// 只写
var chan2 chan<- int
chan2 = make(chan int, 3)
chan2<-2
// num := <-chan2
// send-only type chan<- int
// fmt.Println(chan2)
// 只读
var chan3 <-chan int
// chan3<-6
// receive-only type <-chan int
<-chan3
}
func testSelect(){
intChan := make(chan int, 10)
for i := 0; i <10; i++ {
intChan<-i
}
stringChan := make(chan string ,5)
for i := 0; i <5; i++ {
stringChan<-"hello"+fmt.Sprintf("%d",i)
}
// 传统的方法在遍历的管道时,如果不关闭会阻塞而导致 deadlock
// 在实际开发中,我们可能不确定在什么时候关闭管道
// 可以用select方式解决
for {
select {
case v:=<-intChan:
fmt.Println("intChan数据:",v)
time.Sleep(time.Second)
case v:=<-stringChan:
fmt.Println("stringChan数据:",v)
time.Sleep(time.Second)
default:
fmt.Println("什么也么有!!")
time.Sleep(time.Second)
return
}
}
}
func sayHello() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println("Hello World !")
}
}
func testMap() {
// defer +recover
defer func() {
if err:=recover();err != nil {
fmt.Println("程序报错咯:",err)
}
}()
var myMap map[int]string
myMap[0] = "golang"
}
model/Single.go
package model
import (
"fmt"
"time"
)
/**
* [PrimeCount 求素数(1-300000)]
* @author Jhou Shuai
*/
func PrimeCount() {
start := time.Now().Unix()
// 协程数
var counts int = 8
var num int = 300000
intChan := make(chan int, 10000)
primeChan := make(chan int, 20000)
done := make(chan bool, counts)
go putNum(intChan, num)
for i := 0; i < counts; i++ {
go getPrime(intChan, primeChan, done)
}
// 主线程 关闭管道
go func() {
for i := 0; i < counts; i++ {
<-done
}
close(primeChan)
}()
// 遍历结果primeChan管道,打印输出
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Printf("素数:%v \n", res)
}
end := time.Now().Unix()
fmt.Println("主线程退出~~~完成!耗时:", end-start, "秒")
}
// 向管道中写入 300000个数
func putNum(intChan chan int, num int) {
for i := 1; i <= num; i++ {
intChan <- i
}
// 关闭管道
close(intChan)
}
func getPrime(intChan chan int, primeChan chan int, done chan bool) {
// 素数
var flag bool
for {
num, ok := <-intChan
if !ok {
// 管道中取不到数据咯。退出
break
}
flag = true
for i := 2; i < num; i++ {
if num%i == 0 {
// 不是素数,跳出循环
flag = false
break
}
}
if flag {
primeChan <- num
}
}
// 当前协程工作完毕
done <- true
}
网友评论