Golang使用Channel

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 main

import (
	"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 := 1
	for i := 1; i <= n; i++ {
		res *= i
	}
	//把结果放入到map中
	//concurrent map writes
	//加锁
	lock.Lock()
	myMap[n] = res
	lock.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 main

import "fmt"

/**
Channel使用注意事项
1.Channel中只能存放指定的数据类型
2.Channel数据放满后,就不能放入了
3.如果从Channel中取出数据后,可以继续放入
4.在没有使用协程的情况下,如果Channel数据取完了,再取,就会报dead lock
*/
//演示管道需求
//channel相当于queue

func main() {
	//1.创建一个存放3个int类型的管道
	var intChan chan int
	//capacity=3
	intChan = make(chan int, 3)

	//向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num

	//4.看看管道的长度和容量
	fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))

	//5.从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.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 main

import "fmt"

// 1.关闭管道,管道不能被写入数据,但是可以取出数据
func test1() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	intChan <- 300
	fmt.Println("Hello")

}

// 遍历管道
func test2() {
	intChan := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan <- i * 2
	}
	//这个方法的前提是Channel要关闭,否则出现deadlock
	close(intChan)
	for v := range intChan {
		fmt.Println("v=", v)
	}
}

func main() {
	test2()
}

5.goroutine和channel协同工作案例

package main

import (
	"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 <- i
		time.Sleep(time.Second)
		fmt.Println("writeData=", i)
	}
	close(intChan)
}
func readData(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		time.Sleep(time.Second)
		fmt.Println("readData=", v)
	}
	//读完数据后
	exitChan <- true
	close(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 := <-exitChan
		if 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 main

import (
	"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 := <-intChan
		if !ok {
			break
		}
		flag := true
		for i := 2; i < num; i++ {
			if num%i == 0 {
				flag = false
				break
			}
		}
		if flag {
			//放入素数管道
			primeChan <- num
		}
	}
	fmt.Println("有一个primeNum协程因为取不到数据,退出")
	//这里我们还不能关闭primeChan
	//向exitChan写入true
	exitChan <- 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取出数据并判断是否为素数
	//如果是素数直接放入primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}

	go func() {
		//这里主线程要进行处理
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		//从exitChan取出了4个结果,可以放心的关闭primeChan
		end := time.Now().Unix()
		fmt.Println("使用协程耗时=", end-start)
		close(primeChan)
	}()

	//遍历primeNum,把结果取出
	for {
		res, ok := <-primeChan
		if !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 main

import (
	"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
		//会自动的到下一个case
		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("都读取不到了,不玩了")
			return
		}
	}
}

 8.goroutine中使用recover可以解决协程中出现panic

package main

import (
	"fmt"
	"time"
)

// goroutine中使用recover可以解决协程中出现panic,导致程序崩溃问题
func sayHello() {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		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" //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)
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值