GO语言学习之路22

2022/02/13
今天不上班,看得早一点,终于可以在晚上12点前发笔记了,记录下
package main

import "fmt"

/*
	channel介绍
	1.channel本质就是一个数据结构--队列
	2.数据是先进先出,FIFO
	3.线程安全,多goroutine访问时,不需要加锁,就是说channel本身就是线程安全的
	4.channel是有类型的,一个string的channel只能存放string类型的数据
	5.一个channel只能放相同的数据类型
	6.channel的数据放满后,就不能再放了
	7.在没有使用协程的情况下,如果管道没有数据,再取数据会报错



	channel定义
	var 变量名 chan 数据类型
	demo
		var intChan chan int


	1.channel是引用类型
	2.channel必须初始化才能写入数据,即make后才能使用
	3.管道是有类型的,intChan只能写入整数int
*/

func main() {
	//1.创建一个可以存放3个int类型的管道
	var intChan chan int
	intChan = make(chan int, 3)
	//管道和map不一样,map容量可以自动增加,但是管道定义的是多少就是多少
	//容量不会自动增加

	//2.看看intChan是什么
	fmt.Println("intChan: ", intChan)       //output: intChan:  0xc000152080
	fmt.Println("intChan本身的地址: ", &intChan) //output: intChan:  0xc00014c018

	//3.向管道写入数据,向管道写数据不能超过容量
	intChan <- 10
	num := 2
	intChan <- num

	//4.从管道中读取数据,
	//在没有使用协程的情况下,如果管道没有数据,再取数据会报错
	var num2 int
	num2 = <-intChan
	<-intChan                    //取数据但是不接收,相当于不要这个数据了
	fmt.Println("num2 = ", num2) //output: num2 =  10

	//5.channel的关闭
	//使用内置函数close,关闭管道以后,不能再往channel推入数据,但是可以读取数据
	close(intChan)
	//intChan <- 300 //panic: send on closed channel
	n1 := <-intChan
	fmt.Println("n1 = ", n1) //output: n1 =  0

	//6.channel的遍历
	/*
		channel支持for-range的方式进行遍历
		1.在遍历时,如果channel没有关闭,则会出现deadlock的错误
		2.在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
	*/
	intChan2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan2 <- i * 2
	}
	//遍历
	/*
		我们发现,这种方式只推出了50个数据,这是因为每推出一个数据
		我们的channel的len就-1
	*/
	for i := 0; i < len(intChan2); i++ {
		fmt.Printf("intChan2[%d] = %v\n", i, <-intChan2)
	}
	for len(intChan2) > 0 {
		fmt.Printf("%v\n", <-intChan2)
	}

	intChan3 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan3 <- i * 2
	}

	//在遍历时,如果channel没有关闭,则会出现deadlock的错误
	//会取出来全部的数据,但是没有关闭管道,不知道什么时候是结尾,
	//关闭管道提供EOF,遍历到EOF停止
	// for v := range intChan3 {//有deadlock
	// 	fmt.Println("v = ", v)
	// }
	fmt.Println("~~~~下面的是正确的方式~~~~")
	close(intChan3)
	for v := range intChan3 {
		fmt.Println("v = ", v)
	}
}
package main

import (
	"fmt"
	"strconv"
)

/*
	1.创建一个mapChan,最多可以存放10个map[string]string的key-val
	演示写入和读取

	2.创建一个catChan,最多可以存放10个Cat结构体变量,
	演示写入和读取的用法

	3.创建一个catChan2,最多可以存放10个*Cat变量,
	演示写入和读取的用法

	4.创建一个allChan,最多可以存放10个任意数据类型变量,
	演示写入和读取的用法

	//这些题都是一样的,做了两个就可以了-
*/

func test1() {
	// 1.创建一个mapChan,最多可以存放10个map[string]string的key-val
	// 演示写入和读取
	fmt.Println("test1")
	mapChan := make(chan map[string]string, 10)
	for i := 0; i < 10; i++ {
		fmt.Println("开始写入")
		myMap := make(map[string]string)
		myStr := strconv.Itoa(i)
		myMap[myStr] = "hello " + myStr
		mapChan <- myMap
	}
	close(mapChan)
	for v := range mapChan {
		fmt.Println("取出的v= ", v)
	}
}

type Cat struct {
	Name string
	Age  int
}

func main() {
	test1()
	//定义一个可以存放任意数据类型的管道
	//var allChan chan interface{}
	allChan := make(chan interface{}, 3)
	allChan <- 10
	allChan <- "tom jack"
	cat := Cat{"小花猫", 4}
	allChan <- cat

	//我们希望获得到管道中的第三个元素,则先将前两个推出
	<-allChan
	<-allChan
	newCat := <-allChan
	fmt.Printf("从管道中取出的cat的类型是: %T, newCat = %v\n", newCat, newCat)
	//fmt.Println("newCat.name = %v", newCat.Name) //newCat.Name undefined (type interface {} is interface with no methods)
	a, ok := newCat.(Cat) //要用断言,看下这个interface{}变量是否可以转换成我们需要的变量类型
	if ok {
		fmt.Println(a.Name)
	}
}
package main

import (
	"fmt"
	"time"
)

/*
	1.开启一个writeData协程,向管道intChan中写入50个整数
	2.开启一个readData协程,从管道intChan中读取writeData写入的数据
	3.writeData和readData操作的是同一个管道
	4.主线程需要等待writeData和readData协程都完成工作才能退出


	如果写入慢而读取快,也不会发生死锁,写入可以慢,但是必须要在写
	反之一样
	如果写入很快而读很慢,管道容量是30,要写入50个数,会先写入30,
	读得慢,就慢慢读,只要检测到在读,慢了也不会死锁
	写管道和堵管道的频率不一致,无所谓

	如果编译器发现一个管道,只有写,没有读,则该管道写满就会阻塞,死锁

*/

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

	//time.Sleep(time.Second * 3)
	for {
		_, ok := <-exitChan
		if !ok { //close会让 ok = false
			break
		}
		//return
	}
}
package main

import (
	"fmt"
	"math/rand"
	"strconv"
)

/*
	1.创建一个persion结构体,name,age,address
	2.使用rand方法配合随机创建10个persion实例,并放入channel中
	3.遍历channel,将各个persion实例的信息显示在终端
*/

type Persion struct {
	Name    string
	Age     int
	Address string
}

func main() {
	persionChan := make(chan Persion, 10)
	var name string
	var age int
	var address string
	for i := 0; i < 10; i++ {
		name = "hello " + strconv.Itoa(rand.Intn(50))
		age = rand.Intn(50)
		address = "world + " + strconv.Itoa(rand.Intn(50))
		per := Persion{name, age, address}
		persionChan <- per
	}
	close(persionChan)
	//遍历channel,打印信息
	for {
		v, ok := <-persionChan
		if !ok {
			break
		}
		fmt.Printf("Name = %v, Age = %v, Address = %v\n", v.Name, v.Age, v.Address)
	}
}
package main

import (
	"fmt"
)

/*
	需求,要求统计1-80000数字中,哪些是素数
	//传统的方法,就是使用一个循环,循环的判断各个数是不是素数

	//使用并发/并行的方式,将统计素数的任务分配个多个(4个)goroutine去完成
		完成任务时间短

	//这里能非常体会到各个协程是在同时进行!!!
*/

func putNum(intChar chan int) {
	for i := 1; i <= 8000; i++ {
		intChar <- i
	}
	//写完了就关闭管道
	close(intChar)
}

func primeNum(intChar chan int, primeChan chan int, exitChan chan bool) {
	//var num int
	var flag bool
	for {
		//time.Sleep(time.Microsecond)
		num, ok := <-intChar
		if !ok {
			break
		}
		flag = true
		//判断是不是素数
		for i := 2; i < num; i++ {
			if num%i == 0 { //说明该num不是素数
				flag = false
				break
			}
		}
		if flag { //如果是素数,就将这个数放入到primeChan
			primeChan <- num
		}
	}
	fmt.Println("有一个协程因为取不到数据已经完成工作了")
	//这里不能关闭管道,有可能别的协程还在处理
	exitChan <- true
}

func main() {
	intChan := make(chan int, 100)
	primeChan := make(chan int, 2000) //放入结果的channel
	exitChan := make(chan bool, 4)    //标识退出的管道
	//开启一个协程,向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管道取出四个结果,这个时候就说明处理完了
		//先关闭保存数据的管道
		close(primeChan)
	}()
	//这里主线程要进行处理
	//遍历我们的exitChan 把结果取出
	for {
		res, ok := <-primeChan //只要在读,无论写得多慢都不影响,所以会阻塞在这
		if !ok {
			break
		}
		fmt.Printf("素数都有 %d \n", res)
	}
	fmt.Println("主线程退出")
}
package main

import "fmt"

/*
	1.启动一个协程,将1-1000的数放入到一个numChan中
	2.启动8个协程,从numChan中取出数字,并计算1+..+n的值,并存放到resChan
	3.最后8个协程完成工作后,再遍历resChan,显示结果
*/

//放入numChan中
func putNum(numChan chan int) {
	for i := 1; i <= 100; i++ {
		numChan <- i
	}
	close(numChan) //写完了
}

func getNum(numChan chan int, resChan chan int, exitChan chan bool) {
	//读取数据
	var retNum int
	for {
		retNum = 0
		v, ok := <-numChan
		if !ok {
			break
		}
		for i := 1; i <= v; i++ {
			retNum += i
		}
		resChan <- retNum
	}
	exitChan <- true //读取完了,在标记的管道上进行标记
}

func main() {
	numChan := make(chan int, 500)
	resChan := make(chan int, 500)
	exitChan := make(chan bool, 8)

	go putNum(numChan)
	//读取数据,8个协程
	for i := 0; i < 8; i++ {
		go getNum(numChan, resChan, exitChan)
	}
	//开启一个匿名协程,任务是找到合适的时机,close放结果的管道
	go func() {
		//一直在标记管道上读取,直到读到所有标记
		for i := 0; i < 8; i++ {
			<-exitChan
		}
		close(resChan)
	}()
	//主线程读取
	for v := range resChan {
		fmt.Println("v = ", v)
	}
	// for {
	// 	v, ok := <-resChan
	// 	if !ok {
	// 		break
	// 	}
	// 	fmt.Println("v = ", v)
	// }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值