文章目录
- 1.并发
- 2. 反射
- 3. json (序列化)
1.并发
Go语言通过编译器运行时(runtime),从语言上支持了并发的特性。Go语言的并发通过goroutine特性完成。
goroutine类似于线程,但是可以根据需要创建多个goroutine并发工作。goroutine是由Go语言的运行时调度完成,而线程是由操作系统调度完成。
1.1 创建goroutine
func main() {
go func() {
for i := 0; i < 100; i++ {
fmt.Println("hello",i)
}
}()
for i := 0; i < 100; i++ {
fmt.Println("nihao",i)
}
}
以上采用匿名函数的方式,也可采用函数的方式,略
1.2 Go语言的协作程序(goroutine)和普通的协作程序(coroutine)
● goroutine可能发生并行执行;但coroutine始终顺序执行。
狭义地说,goroutine可能发生在多线程环境下,goroutine无法控制自己获取高优先度支持;coroutine始终发生在单线程,coroutine程序需要主动交出控制权,宿主才能获得控制权并将控制权交给其他coroutine。
● goroutine间使用channel通信;coroutine使用yield和resume操作。
1.3 goroutine间的通信(channel)
Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个goroutine访问通道进行发送和获取数据。goroutine间通过通道就可以通信。就好比一个阻塞队列。
func main() {
ch1 := make(chan int) //创建存放int型的通道
ch2 := make(chan interface{}) //创建一个空接口类型的通道
ch3 :=make(chan *Student) //创建Student结构体指针类型的通道
}
1.3.1 使用通道发送数据
通道的发送使用特殊的操作符“<-”
通道变量 <- 值
- 通道变量:通过make创建好的通道实例。
- 值:可以是变量、常量、表达式或者函数返回值等。值的类型必须与ch通道的元素类型一致。
ch := make(chan interface{}) //创建一个空接口类型的通道
ch <- 111
ch <- "wowouwo"
运行时发现所有的goroutine(包括main)都处于等待goroutine。也就是说所有goroutine中的channel并没有形成发送和接收对应的代码。
1.3.2 使用通道接收数据
- 通道的收发操作在不同的两个goroutine间进行。
- 接收将持续阻塞直到发送方发送数据。
- 每次接收一个元素。
data := <-ch //阻塞接受
data, ok := <-ch //非阻塞接受 ok代表是否接收到数据
<- ch // 忽略接受
//执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。
//这个方式实际上只是通过通道在goroutine间阻塞收发实现并发同步。
循环接收
for data:=range ch{
}
通道ch是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过for遍历获得的变量只有一个。
func printer(c chan int) {
for {
data := <-c
if data == 0 {
break
}
fmt.Println(data)
}
c <- 0
}
func main() {
c := make(chan int)
go printer(c)
for i := 1; i <= 10; i++ {
c <- i
}
c <- 0
//等待结束
<-c
fmt.Println("结束")
}
单向通道
只能发送的通道类型为chan<-,只能接收的通道类型为<-chan
func main() {
ch := make(chan int)
var chSendOnly chan<- int = ch
data := <-chSendOnly
fmt.Println(data)
}
ch := make(chan int)
//var chSendOnly chan<- int = ch
//data := <-chSendOnly
//fmt.Println(data)
var chReadOnly <-chan int = ch
<-chReadOnly
带缓冲的通道
1.3.3 通道的多路复用
Go语言中提供了select关键字,可以同时响应多个通道的操作。select的每个case都会对应一个通道的收发过程。当收发完成时,就会触发case中响应的语句。多个操作在每次select中挑选一个进行响应。
func RPCClient(ch chan string, req string) (string, error) {
ch <- req
//等待服务器返回
select {
case ack := <-ch:
return ack, nil
case <-time.After(time.Second):
return "", errors.New("超时")
}
}
func RPCServer(ch chan string) {
for {
data := <-ch
fmt.Println("接收到:" + data)
ch <- "had receive"
}
}
func main() {
ch := make(chan string)
go RPCServer(ch)
ack, err :=
RPCClient(ch, "hello")
fmt.Println(ack, err)
}
1.4 同步(lock)
通道内部的实现依然使用了各种锁,因此优雅代码的代价是性能。在某些轻量级的场合,原子访问(atomic包)、互斥锁(sync.Mutex)以及等待组(sync.Wait Group)能最大程度满足需求。
其中介绍等待组:
等待组内部拥有一个计数器,计数器的值可以通过方法调用实现计数器的增加和减少。当我们添加了N个并发任务进行工作时,就将等待组的计数器值增加N。每个任务完成时,这个值减1。同时,在另外一个goroutine中等待这个等待组的计数器值为0时,表示所有任务已经完成。
原子操作:
func main() {
var c int32
wg := sync.WaitGroup{}
//开启100个goroutine
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
tmp := atomic.LoadInt32(&c)
if !atomic.CompareAndSwapInt32(&c, tmp, tmp+1) {
fmt.Println("c 修改失败")
}
}()
}
wg.Wait()
//c的值有可能不等于100,频繁修改变量值情况下,CompareAndSwap操作有可能不成功。
fmt.Println("c : ", c)
}
lock:
需要注意的就是:
lock.Lock()
defer lock.Unlock()
var count int
var lock sync.Mutex
var arthmatic sync.WaitGroup
Increment := func() {
lock.Lock()
defer lock.Unlock()
count++
fmt.Printf("Incrementing: %d\n", count)
}
Decrement := func() {
lock.Lock()
defer lock.Unlock()
count--
fmt.Printf("Decrementing: %d\n", count)
}
for i := 0; i < 5; i++ {
arthmatic.Add(1)
go func() {
defer arthmatic.Done()
Increment()
}()
}
for i := 0; i < 5; i++ {
arthmatic.Add(1)
go func() {
defer arthmatic.Done()
Decrement()
}()
}
arthmatic.Wait()
fmt.Printf("Arthmatic completed!\n")
2. 反射
**反射是指在程序运行期对程序本身进行访问和修改的能力。**程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
修改值:
var num int = 10
rVal := reflect.ValueOf(&num)
fmt.Printf("%v\n", rVal.Kind())
rVal.Elem().SetInt(1)
报错
var str string = "ok"
rval := reflect.ValueOf(str)
rval.SetString("oooo")
fmt.Printf(str)
以下才通过:
var str string = "ok"
rval := reflect.ValueOf(&str)
rval.Elem().SetString("oooo")
fmt.Printf(str)
修改结构体:
type Student struct {
Id int `json:"id"`
Name string `json:"name"`
}
func (s Student) print() {
fmt.Println(s)
}
func main() {
var stu Student = Student{
Id: 0,
Name: "zhaosi",
}
typeOf := reflect.TypeOf(stu)
valueOf := reflect.ValueOf(stu)
kind := valueOf.Kind()
if kind != reflect.Struct {
fmt.Println("不是结构体")
return
}
nums := valueOf.NumField()
fmt.Println("结构体的字段数为:", nums)
for i := 0; i < nums; i++ {
fmt.Printf("Field %d:值为: %v\n", i, valueOf.Field(i))
//获取到标签
tagval := typeOf.Field(i).Tag.Get("json")
fmt.Printf("json tag:%v\n", tagval)
}
numMethod := valueOf.NumMethod()
fmt.Printf("结构体有:%v 个方法\n", numMethod)
//调用结构体的第一个方法
valueOf.Method(0).Call(nil)
}
3. json (序列化)
结构体:
func main() {
s := Student{Id: 1, Name: "lisi"}
data, err := json.Marshal(&s)
if err != nil {
fmt.Println("序列化错误")
}
fmt.Println(string(data))
}
map :
func testmap() {
var a map[string]interface{}
a=make(map[string]interface{})
a["a"] = "b"
a["b"] = 1
data, err := json.Marshal(a)
if err != nil {
fmt.Println("序列化错误")
}
fmt.Println(string(data))
}
反序列化
func reversejson() {
str := "{\"Id\":1,\"Name\":\"lisi\"}"
var a map[string]interface{}
//a = make(map[string]interface{}) 不需要make
err := json.Unmarshal([]byte(str), &a)
if err != nil {
fmt.Println("反序列化失败")
}
fmt.Println(a)
}
语法入门就差不多了。。。