这篇讲解反射和并发,反射功能和在Java中的功能比较类似。go语言中利用goroutine实现开启一个线程(使用go关键字),确实比Java中简洁了不少!
1.反射reflect
反射reflect的知识要点:
- 1、反射可以大大提高程序的灵活性,使得
interface{}
有更大的发挥余地; - 2、反射使用
TypeOf
和ValueOf
函数从接口中获取目标对象信息; - 3、反射会将匿名字段作为独立字段(匿名字段的本质);
- 4、想要利用反射修改对象状态,前提是
interface.data
是settable
,即pointer-interface
; - 5、通过反射可以“动态”调用方法。
反射的定义
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) Hello(){
fmt.Println("Hello world")
}
//传入一个接口
func info(o interface{}) {
//取出这个接口的类型
t:=reflect.TypeOf(o)
fmt.Println("Type",t.Name())
//取出这个接口的值
v:=reflect.ValueOf(o)
fmt.Println("Fields:")
//获取字段信息
//如果要打印出接口中所有值,则需要用到for循环
for i := 0; i < t.NumField(); i++ {
f:=t.Field(i)
val := v.Field(i).Interface()
fmt.Printf("%6s:%v=%v\n",f.Name,f.Type,val)
}
//获取方法信息
for i := 0; i < t.NumMethod(); i++ {
m:=t.Method(i)
fmt.Printf("%6s:%v\n",m.Name,m.Type)
}
}
func main() {
//定义一个User类型并初始化
u:=User{1,"Michaeljian",25}
info(u)
}
匿名字段
package main
import (
"reflect"
"fmt"
)
type User struct {
Id int
Name string
Age int
}
type Manager struct {
//匿名字段
User
title string
}
func main() {
m:=Manager{User:User{1,"Michaeljian",25},title:"做一个有情怀的程序员"}
t:=reflect.TypeOf(m)
//取匿名字段
fmt.Printf("%#v\n",t.Field(0))
fmt.Printf("%#v\n",t.FieldByIndex([]int{0}))
//取匿名字段中的Id字段
fmt.Printf("%#v\n",t.FieldByIndex([]int{0,0}))
}
通过反射动态调用方法
package main
import (
"reflect"
"fmt"
)
type User struct {
Id int
Name string
Age int
}
func (u User)Hello(name string){
fmt.Println("Hello",name,"my name is ",u.Name)
}
func main() {
u:=User{1,"Michaeljian",25}
v:=reflect.ValueOf(u)
//通过方法名来调用
mv:=v.MethodByName("Hello")
//通过反射传入name值
agrs:=[]reflect.Value{reflect.ValueOf("Jerry")}
//调用
mv.Call(agrs)
}
2.并发concurrency
go并发的知识要点:
- 1、
goroutine
是由Go运行时环境管理的轻量级线程;goroutine奉行通过通信来共享内存,而不是共享内存来通信; - 2、
Channel
通道(引用类型)知识点如下:- 1、
Channel
是goroutine
沟通的桥梁,大都是阻塞同步的; - 2、通过
make
创建,close
关闭; - 3、可以使用
for range
来迭代不断操作channel
; - 4、可以设置单向或双向通道;
- 5、可以设置缓存大小,在未被填满前不会发生阻塞。
- 1、
- 3、
Select
知识点如下:- 1、可以处理一个或多个
channel
的发送与接收; - 2、同时有多个可用的
channel
时按随机顺序处理; - 3、可用空的
select
来阻塞main
函数,可设置超时。
- 1、可以处理一个或多个
goroutine的定义
使用go关键字定义goroutine(也就是开启一个线程)。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
//使用go关键字定义goroutine
go say("world")
say("hello")
}
使用channel
channel是有类型的管道,可以用channel操作符<-
对其发送或者接收值。
//(“箭头”就是数据流的方向。)
ch <- v // 将 v 送入 channel ch。
v := <-ch // 从 ch 接收,并且赋值给 v。
和 map 与 slice 一样,channel 使用前必须创建:
ch := make(chan int)
默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
package main
import "fmt"
func main() {
//定义一个channel,其类型为int
c:=make(chan int)
//使用匿名函数
go func() {
fmt.Println("Go Go Go !")
//将数字2放入channel中
c <- 2
//关闭channel
close(c)
}()
//遍历channel中的值
for v := range c{
fmt.Println(v)
}
}
使用select
select
语句使得一个goroutine
在多个通讯操作上等待。select
会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
//利用select管理多个channel
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
//创建两个channel
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}