1.使用协程
package main//使用协程
import ("fmt""strconv""time"
)func test() {for i := 1; i <= 10; i++ {fmt.Println("test() hello world" + strconv.Itoa(i))time.Sleep(time.Second)}
}// 主线程和test协程同时运行
func main() {go test() //开启了一个协程for i := 1; i <= 10; i++ {//strconv.Itoa函数:将整型转换为字符串fmt.Println("main() hello Golang" + strconv.Itoa(i))time.Sleep(time.Second)}
}
main() hello Golang1
test() hello world1
main() hello Golang2
test() hello world2
test() hello world3
main() hello Golang3
main() hello Golang4
test() hello world4
test() hello world5
main() hello Golang5
main() hello Golang6
test() hello world6
test() hello world7
main() hello Golang7
main() hello Golang8
test() hello world8
test() hello world9
main() hello Golang9
main() hello Golang10
test() hello world10
2.使用20个协程计算1到20各个数的阶乘,把结果放到map中
package mainimport ("fmt""sync""time"
)//计算1-20的各个数的阶乘,并且把各个数的阶乘放入到map中
//最后显示出来,要求使用goroutine完成//思路:1.编写一个函数,计算各个数的阶乘,并放入到map中
//2.我们启动的协程多个,统计的结果放入到map中
//3.map应该做出一个全局变量var (myMap = make(map[int]int, 20)//lock是一个全局的互斥锁//Mutex:互斥lock sync.Mutex
)func test(n int) {res := 1for i := 1; i <= n; i++ {res *= i}//把结果放入到map中//concurrent map writes//加锁lock.Lock()myMap[n] = reslock.Unlock()
}func main() {//开启多个协程完成这个任务for i := 1; i <= 20; i++ {go test(i)}//让主线程休眠,因为主线程执行完后,协程就会中止time.Sleep(time.Second * 5)//输出结果,遍历这个结果//lock.Lock()for i, v := range myMap {fmt.Printf("map[%d]=%d\n", i, v)}//lock.Unlock()
}
map[3]=6
map[7]=5040
map[10]=3628800
map[11]=39916800
map[19]=121645100408832000
map[20]=2432902008176640000
map[13]=6227020800
map[9]=362880
map[16]=20922789888000
map[4]=24
map[5]=120
map[6]=720
map[8]=40320
map[15]=1307674368000
map[18]=6402373705728000
map[17]=355687428096000
map[14]=87178291200
map[1]=1
map[2]=2
map[12]=479001600
3.Channel的使用
(1).Channel中只能存放指定的数据类型
(2).Channel数据放满后,就不能放入了
(3).如果从Channel中取出数据后,可以继续放入
(4).在没有使用协程的情况下,如果Channel数据取完了,再取,就会报dead lock
package mainimport "fmt"/**
Channel使用注意事项
1.Channel中只能存放指定的数据类型
2.Channel数据放满后,就不能放入了
3.如果从Channel中取出数据后,可以继续放入
4.在没有使用协程的情况下,如果Channel数据取完了,再取,就会报dead lock
*/
//演示管道需求
//channel相当于queuefunc main() {//1.创建一个存放3个int类型的管道var intChan chan int//capacity=3intChan = make(chan int, 3)//向管道写入数据intChan <- 10num := 211intChan <- num//4.看看管道的长度和容量fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))//5.从管道中读取数据var num2 intnum2 = <-intChanfmt.Println("num2=", num2)fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))
}
channel len=2 cap=3
num2= 10
channel len=1 cap=3
4.遍历管道
package mainimport "fmt"// 1.关闭管道,管道不能被写入数据,但是可以取出数据
func test1() {intChan := make(chan int, 3)intChan <- 100intChan <- 200close(intChan)intChan <- 300fmt.Println("Hello")}// 遍历管道
func test2() {intChan := make(chan int, 100)for i := 0; i < 100; i++ {intChan <- i * 2}//这个方法的前提是Channel要关闭,否则出现deadlockclose(intChan)for v := range intChan {fmt.Println("v=", v)}
}func main() {test2()
}
5.goroutine和channel协同工作案例
package mainimport ("fmt""time"
)/*
*
goroutine和channel协同工作案例
1.开启一个writeData协程,向管道intChan输入50个整数
2.开启一个readData协程,从管道intChan中读取writeData写入数据
3.writeData和readData操作的都是同一条管道
4.主线程需要等待writeData和readData协程都完成工作才退出
*/
func writeData(intChan chan int) {for i := 1; i <= 50; i++ {intChan <- itime.Sleep(time.Second)fmt.Println("writeData=", i)}close(intChan)
}
func readData(intChan chan int, exitChan chan bool) {for {v, ok := <-intChanif !ok {break}time.Sleep(time.Second)fmt.Println("readData=", v)}//读完数据后exitChan <- trueclose(exitChan)
}
func main() {intChan := make(chan int, 50)exitChan := make(chan bool, 1)go writeData(intChan)go readData(intChan, exitChan)//time.Sleep(time.Second * 10)for {_, ok := <-exitChanif ok {break}}
}
writeData= 1
readData= 1
readData= 2
writeData= 2
readData= 3
writeData= 3
readData= 4
writeData= 4
readData= 5
writeData= 5
readData= 6
writeData= 6
readData= 7
writeData= 7
readData= 8
writeData= 8
readData= 9
writeData= 9
readData= 10
writeData= 10
readData= 11
writeData= 11
readData= 12
writeData= 12
readData= 13
writeData= 13
readData= 14
writeData= 14
readData= 15
writeData= 15
readData= 16
writeData= 16
readData= 17
writeData= 17
readData= 18
writeData= 18
readData= 19
writeData= 19
readData= 20
writeData= 20
readData= 21
writeData= 21
readData= 22
writeData= 22
readData= 23
writeData= 23
readData= 24
writeData= 24
readData= 25
writeData= 25
readData= 26
writeData= 26
readData= 27
writeData= 27
readData= 28
writeData= 28
readData= 29
writeData= 29
readData= 30
writeData= 30
readData= 31
writeData= 31
readData= 32
writeData= 32
readData= 33
writeData= 33
readData= 34
writeData= 34
readData= 35
writeData= 35
readData= 36
writeData= 36
readData= 37
writeData= 37
readData= 38
writeData= 38
readData= 39
writeData= 39
readData= 40
writeData= 40
readData= 41
writeData= 41
readData= 42
writeData= 42
readData= 43
writeData= 43
readData= 44
writeData= 44
readData= 45
writeData= 45
readData= 46
writeData= 46
readData= 47
writeData= 47
readData= 48
writeData= 48
readData= 49
writeData= 49
readData= 50
writeData= 50
6.协程求素数
package mainimport ("fmt""time"
)//协程求素数
//要求统计1-200的数字中,哪些是素数?// 使用并行的方式,将统计素数的任务分配给多个(4个)goroutine去完成
func putNum(intChan chan int) {for i := 1; i <= 200; i++ {intChan <- i}//关闭close(intChan)
}// 从intChan取出数据,判断是否为素数,如果是,就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {for {num, ok := <-intChanif !ok {break}flag := truefor i := 2; i < num; i++ {if num%i == 0 {flag = falsebreak}}if flag {//放入素数管道primeChan <- num}}fmt.Println("有一个primeNum协程因为取不到数据,退出")//这里我们还不能关闭primeChan//向exitChan写入trueexitChan <- true
}func main() {intChan := make(chan int, 1000)//放入结果primeChan := make(chan int, 10000)//标识退出的管道exitChan := make(chan bool, 4)start := time.Now().Unix()//开启一个协程,向intChan放入1-8000个数go putNum(intChan)//开启4个协程向intChan取出数据并判断是否为素数//如果是素数直接放入primeChanfor i := 0; i < 4; i++ {go primeNum(intChan, primeChan, exitChan)}go func() {//这里主线程要进行处理for i := 0; i < 4; i++ {<-exitChan}//从exitChan取出了4个结果,可以放心的关闭primeChanend := time.Now().Unix()fmt.Println("使用协程耗时=", end-start)close(primeChan)}()//遍历primeNum,把结果取出for {res, ok := <-primeChanif !ok {break}fmt.Printf("素数=%d\n", res)}fmt.Println("main()退出")
}
素数=1
素数=2
有一个primeNum协程因为取不到数据,退出
有一个primeNum协程因为取不到数据,退出
有一个primeNum协程因为取不到数据,退出
素数=3
有一个primeNum协程因为取不到数据,退出
使用协程耗时= 0
素数=5
素数=7
素数=11
素数=13
素数=17
素数=19
素数=23
素数=29
素数=31
素数=37
素数=41
素数=43
素数=47
素数=53
素数=59
素数=61
素数=67
素数=71
素数=73
素数=79
素数=83
素数=89
素数=97
素数=101
素数=103
素数=107
素数=109
素数=113
素数=127
素数=131
素数=137
素数=139
素数=149
素数=151
素数=157
素数=163
素数=167
素数=173
素数=179
素数=181
素数=191
素数=193
素数=197
素数=199
main()退出
7.使用select解决管道阻塞问题
package mainimport ("fmt"
)//使用select解决管道阻塞问题func main() {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", 5)}//传统的方法遍历管道,如果不关闭会阻塞,会遭遇deadlock//有可能不好确定什么时候关闭管道//使用select解决for {select {//注意:如果intChan一直没有关闭,不会一直阻塞而deadlock//会自动的到下一个casecase v := <-intChan:fmt.Println("从intChan读取数据 ", v)//time.Sleep(time.Second)case v := <-stringChan:fmt.Println("从stringChan读取数据 ", v)//time.Sleep(time.Second)default:fmt.Println("都读取不到了,不玩了")return}}
}
8.goroutine中使用recover可以解决协程中出现panic
package mainimport ("fmt""time"
)// goroutine中使用recover可以解决协程中出现panic,导致程序崩溃问题
func sayHello() {for i := 0; i < 10; i++ {time.Sleep(time.Second)fmt.Println("Hello world")}}func test() {//这里我们使用defer+recoverdefer func() {//捕获test抛出的panic//捕获了以后,这个协程发生错误不影响其他协程if err := recover(); err != nil {fmt.Println("test() 发生错误", err)}}()//定义了一个map,让它发生错误var myMap map[int]stringmyMap[0] = "Golang" //error
}
func main() {go sayHello()//panic: assignment to entry in nil map//报错引起整个程序崩溃go test()for i := 0; i < 10; i++ {fmt.Println("main() ok=", i)time.Sleep(time.Second)}
}