当前位置:网站首页>go入门篇 (5)
go入门篇 (5)
2022-07-27 11:02:00 【初心魏】
一、协程
1.1 go协程的特点
- 独立的栈空间
- 共享程序的堆空间
- 调度由用户控制
- 协程是轻量级的线程
1.2 协程的简单案例
func showNum(){
for i:= 0; i < 10; i++{
fmt.Println("test" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main(){
//这里就开启了协程
go showNum()
for i:= 0; i < 10; i++{
fmt.Println("main" + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
结果:
main0
test0
test1
main1
main2
...
test9
main9
1.2 MPG模式
- M 操作系统中的线程
- P 协程运行所需的上下文环境
- G 协程
1.3 设置运行的cpu个数
func main(){
cpunum := runtime.NumCPU()
fmt.Println("正在运行的cpu个数", cpunum)
//setCpuNum := cpunum - 1
runtime.GOMAXPROCS(3)
fmt.Println(runtime.NumCPU())
}
结果:
正在运行的cpu个数 4
4
1.4 测试有几个协程争夺资源
- go build -race test.go
1.5 全局变量加锁解决资源竞争
- 案例不是很严谨
var(
ticket int = 100
lock sync.Mutex
)
func saleTicket() {
lock.Lock()
for ticket > 0 {
ticket--
fmt.Printf("当前剩余的票数是:%d", ticket)
fmt.Println()
}
if ticket == 0 {
fmt.Println("无票可卖")
}
lock.Unlock()
}
func main(){
for i := 0; i < 12; i++ {
go saleTicket()
}
time.Sleep(time.Second * 5)
}
1.6 解决协程中可能报错的问题
- defer + recocer
func showNum(){
for i := 0 ; i< 10; i++{
time.Sleep(time.Second)
fmt.Println("test输出了数字", i)
}
}
func test(){
defer func(){
err := recover()
if err != nil {
fmt.Println("出现了问题")
}
}()
var mymap map[int]string
mymap[0] = "hahaha"
}
func main() {
go showNum()
go test()
for i := 0 ; i< 10; i++{
time.Sleep(time.Second)
fmt.Println("main输出了数字", i)
}
}
二、channel
2.1 channe的介绍
- 本质是队列
- 数据是先进先出
- 本身是线程安全的,不需要加锁,即多个协程竞争同一管道资源时,不会发生资源争抢的情况
- 通道是有类型的,一个string类型的通道只能存放string类型的数据
2.2 channel中添加数据
type Cat struct{
name string
age int
address string
}
func main(){
var mychannel chan interface{
} = make(chan interface{
}, 3)
fmt.Println(len(mychannel), cap(mychannel))
mychannel <- "jack"
mychannel <- 11
fmt.Println(len(mychannel), cap(mychannel))
var cat1 Cat = Cat{
name: "tom",
age : 6,
address: "陕西",
}
mychannel <- cat1
<- mychannel
<- mychannel
mycat := <- mychannel
fmt.Printf("%T, %v",mycat, mycat)
//如果要读取cat中的属性,需要进行类型断言才可以
res := mycat.(Cat)
fmt.Println()
fmt.Println(res.name)
}
2.3 关闭channel
- 关闭通道不能添加数据,但是可以弹出数据
func main(){
var mychanel chan int = make(chan int, 3)
mychanel <- 32
//关闭通道不能添加数据,但是可以读取数据
close(mychanel)
num := <- mychanel
mychanel <-76
fmt.Println(num)
fmt.Println(len(mychanel), cap(mychanel))
}
2.4 for-range遍历channel
- 遍历channel前必须关闭channel,否则会出错
- 注意添加的数据不能超过channel的容量,不然遍历不会出结果
type Cat struct{
name string
age int
address string
}
func main(){
var mychanel chan int = make(chan int, 10)
mychanel <- 32
mychanel <- 33
mychanel <- 34
mychanel <- 35
mychanel <- 36
//关闭通道不能添加数据,但是可以读取数据
close(mychanel)
for v := range mychanel{
fmt.Println(v)
}
}
2.5 channel和协程的配合
func writeData(writechan chan int){
for i := 0; i < 3; i++{
writechan <- i
fmt.Println("向管道中写了数据",i)
}
close(writechan)
}
func readData(readchan chan int, exitchan chan bool){
for {
v, ok := <- readchan
fmt.Println(ok)
if !ok{
break
}
fmt.Println("从管道中读取了数据:",v)
}
fmt.Println("haha")
exitchan <- true
close(exitchan)
}
func main(){
var writechan chan int = make(chan int, 3)
var exitchan chan bool = make(chan bool, 1)
go writeData(writechan)
go readData(writechan, exitchan)
//使用exitchan来控制主线程的结束运行
val := <- exitchan
fmt.Println(len(exitchan))
if val == true{
fmt.Println("主线程可以结束运行了")
}
fmt.Println()
}
2.6 channel的阻塞机制
- 通道中没有数据读取时会阻塞
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
C:/Users/Administrator/go/src/awesomeProject1/src/view/locak.go:7 +0x3c
- 向通道中写的数据个数大于通道容量会阻塞
2.7 协程求素数
- 多个协程求素数
func putdata(putchan chan int){
for i := 0; i < 80; i++{
putchan <- i
}
close(putchan)
}
func printdata(putchan chan int, printchan chan int, exirchan chan bool){
for {
num, ok := <-putchan
if !ok {
break
}
//判断num是不是素数
if num != 0 && num != 1{
var flag bool = true
for j := 2; j < num; j++{
if num % j == 0 {
flag = false
break
}
}
if flag{
//是素数,放入管道
printchan <- num
fmt.Println("素数:", num)
}
}
}
//判断完所有的素数
fmt.Println("有协程取不到数")
exirchan <- true
}
func main() {
var putchan chan int = make(chan int, 1000)
var printchan chan int = make(chan int, 4000)
var exitchan chan bool = make(chan bool, 4)
go putdata(putchan)
for i := 0; i < 5; i++ {
go printdata(putchan, printchan, exitchan)
}
go func() {
for i := 0; i < 5; i++ {
<-exitchan
}
fmt.Println("主线程结束")
close(printchan)
}()
time.Sleep(time.Second*5)
fmt.Println("主线程退出")
}
2.8 管道设置为只写或者只读
func main() {
//设置为只写
var mychan chan <- int = make(chan int, 3)
//设置为只读
var mychan1 <- chan int = make(chan int, 3)
}
2.9 select读取管道的数据
- 普通的管道遍历前需亚关闭管道,但是使用select就可以不用关闭管道而读取到里面的数据
func main() {
var mychan chan int = make(chan int, 5)
for i :=0 ; i < 5; i++{
mychan <- i
}
flag:
for{
select {
case num := <- mychan:
fmt.Println(num)
default:
fmt.Println("数据都已经读完了")
break flag
}
}
}
result:
0
1
2
3
4
数据都已经读完了
三、反射
3.1 变量,interface,reflect.value之间的转换
3.1.1 interface{}转换为reflect.value
ref := reflect.valueof(b)
3.1.2 reflect.value转换为interface{}
ival := ref.interface()
3.1.3 interface转换为原来的类型
- 使用类型断言
inter.(stu)
3.2 通过反射获取变量的类型
var stu Student = Student{
name:"jack",
age:23,
address: "西安",
}
res := reflect.TypeOf(stu)
3.3 通过反射操作基本变量
func changeType(num interface{
}){
tp := reflect.TypeOf(num)
//va是value类型的
va := reflect.ValueOf(num)
fmt.Println(tp.Name())
fmt.Println(va.Int())
//将value类型的转化成interface类型
iv := va.Interface()
//使用类型断言将interface{}类型转化为int类型
res := iv.(int)
fmt.Printf("%T %v", res, res)
}
func main(){
var num int = 34
changeType(num)
}
3.4 获取结构体的其他信息
- 结构体名称,字段个数,字段的名称,字段对应的值
type Student struct{
name string
age int
address string
}
func main() {
var stu Student = Student{
name: "jack",
age: 23,
address: "西安",
}
ty := reflect.TypeOf(stu)
va := reflect.ValueOf(stu)
//获取结构体的名称
fmt.Println(reflect.TypeOf(stu).Name())
//获取结构体中定义的字段的个数
fmt.Println(reflect.TypeOf(stu).NumField())
//获取结构体中每一个字段的名称,和其对应的值
for i := 0; i < ty.NumField(); i++ {
key := ty.Field(i)
value := va.Field(i)
fmt.Println("key是:",key.Name, "value是:",value)
}
}
3.5 匿名结构体的反射
- 和普通结构体无任何差别
type Student struct{
name string
age int
address string
}
type Primary struct{
Student
hobby string
age int
}
func main() {
var p Primary = Primary{
}
p.Student = Student{
name: "jack",
address: "xian",
age: 23,
}
p.age = 34
tp := reflect.TypeOf(p)
va := reflect.ValueOf(p)
//结构体名称
fmt.Println(tp.Name())
//字段个数
fmt.Println(tp.NumField())
//%#v展示Student的详细信息
fmt.Printf("%#v",tp.Field(0))
fmt.Println()
for i := 0; i < tp.NumField(); i ++{
fmt.Printf("key是:%v\t\t\t,", tp.Field(i).Name)
fmt.Printf("value是:%v\t\t\t",va.Field(i))
fmt.Println()
}
}
3.6 判断是否为struct类型
- p是结构体对象
kind := reflect.TypeOf(p).Kind()
3.7 修改结构体对象中的字段的值
type Student struct{
Aame string
Age int
Address string
}
func main() {
var stu *Student = &Student{
Aame: "jack",
Address: "xian",
Age: 23,
}
//利用反射修改值必须是指针类型
//判断是否是指针类型
if reflect.TypeOf(stu).Kind() != reflect.Ptr{
return
}
va := reflect.ValueOf(stu)
//获取想要修改的字段
address := va.Elem().FieldByName("Address")
if address.Kind() == reflect.String{
address.SetString("new address changan")
}
fmt.Println(stu.Address)
}
3.8 修改普通类型变量的值
var name string = "西安"
value := reflect.ValueOf(&name)
value.Elem().SetString("jack")
fmt.Println(name)
3.9 修改结构体方法的一些参数
3.10 查看并且调用结构体的方法
func main() {
var mycat Cat = Cat{
Name: "hojju",
Age: 13,
Address: "西安",
}
tp := reflect.TypeOf(mycat)
vl := reflect.ValueOf(mycat)
for i := 0; i < tp.NumMethod(); i ++ {
//得到的函数方法是按照函数名的ASCLL码排序的
fmt.Println(tp.Method(i))
}
var params []reflect.Value
params = append(params,reflect.ValueOf("jack"))
params = append(params,reflect.ValueOf(12))
params = append(params,reflect.ValueOf("陕西西安"))
//调用参数
params = vl.Method(1).Call(params)
for val := range params{
fmt.Println(val)
}
}
3.11 得到结构体tag字段的值
type Cat struct {
Name string`json:"qweqename"`
Age int`json:"age"`
Address string`json:"address"`
}
func main() {
var mycat Cat = Cat{
Name: "hojju",
Age: 13,
Address: "西安",
}
tp := reflect.TypeOf(mycat)
name := tp.Field(0).Tag.Get("json")
fmt.Println(name)
}
qweqename
3.12 注意事项
用反射修改机构体中某一属性的值的时候,该结构体必须是指针类型
修改结构体值的时候,该属性开头字母必须是大写的
边栏推荐
- C programming language (2nd Edition) -- Reading Notes -- 1.5.4
- Keil MDK编译出现..\USER\stm32f10x.h(428): error: #67: expected a “}“错误的解决办法
- Matlab draws Bode diagram with time delay system
- 箱型图介绍
- 局域网SDN硬核技术内幕 24 展望未来——RDMA(中)
- SMA TE: Semi-Supervised Spatio-Temporal RepresentationLearning on Multivariate Time Series
- 剑指 Offer 笔记: T58 - I. 翻转单词顺序
- 剑指 Offer 笔记: T39. 数组中出现次数超过一半的数字
- USB 网卡驱动数据流
- Difference quotient approximation of wechat quotient
猜你喜欢
随机推荐
ZABBIX custom monitoring items
The C programming language (2nd) -- Notes -- 1.7
SMA TE: Semi-Supervised Spatio-Temporal RepresentationLearning on Multivariate Time Series
Principle of PWM and generation of PWM wave
Detailed explanation of hash table
How to make a graph? Multiple subgraphs in a graph are histogram (or other graphs)
局域网SDN技术硬核内幕 13 从局域网到互联网
STM32编译出现error: L6235E: More than one section matches selector - cannot all be FIRST/L
Open source Flink has datastream connector written with holo or Flink SQL Conn
Firewall firewall
Ask the big guys, is there transaction control for using flick sink data to MySQL? If at a checkpoint
多种进制之间的转换
Arduino常见供电问题与解决
C programming language (2nd Edition) -- Reading Notes -- 1.3
Keil MDK compilation appears..\user\stm32f10x H (428): error: # 67: expected a "}" wrong solution
Modelarts image classification and object detection
The difference between microcomputer and single chip microcomputer
Maker paper data search training notes
为什么TCP三次握手的时候ACK=Seq+1
Temporary use of solo, difficult choice of Blog








