前言
我就不展开说要写什么了,最近在研读论文中的代码,其中很多知识都是没有接触过的,就边看边学,最近积累了一些知识点,现在我把它总结一些,主要涉及的是golang。
go init 函数
此函数是用来进行初始化工作的,在程序开始执行时按照声明的顺序被自动调用,比如说一些变量需要初始化,则可以采用这种方式,举一个在源码中的例子:
var profile string
var tls_eval bool
var split_to_buckets uint
func init() {
flag.StringVar(&profile, "profile", "", "Should open pprof on ip:port")
flag.BoolVar(&tls_eval, "tls-eval", false, "Split according to .timestamp and filter data")
flag.UintVar(&split_to_buckets, "buckets", 0, "Split data to the given amount of buckets")
flag.Parse()
}
这里的init
函数就对其上面声明的变量进行了初始化,可能有的朋友看不懂函数中的内容,在这里做简单说明:
flag
包是用来解析命令行参数的,类似于os.argv
- flag.StringVar
定义了一个指定名字、默认值和用法说明的string
标签。
usage
:./gofilename
-参数名xxx
(代表要输入的内容)
以此类推,flag.xxxVar
,都代表一个意思,将该变量绑定到输入的内容上,该内容的类型决定了xxx
的类型。
- flag.parse()
这个函数必须调用,不然无法解析键入的参数
map
map时golang中的一个无序的键值对的集合,是通过哈希表来实现的。
-
map中的元素不是一个变量,因此不能对map的元素进行取地址操作
-
遍历的方法:
for k,v := mapexample{
k:是键
v:是值
}
- 声明
mapexample := make(map[string]int)
在这里我还想说一点,在源码中碰到的:
var parsed_json map[string]interface{}
//反序列化成map
if json.Unmarshal([]byte(json_line), &parsed_json) != nil {
log.Printf("Failed parsing line to json object, affected line:\n%s\n", json_line)
break
}
//然后再取map中的元素
comsys_tool, ok := parsed_json["comsys-tool"].(string)
if !ok {
log.Printf("No comsys-tool in json object")
break
}
这时会返回两个值一个是key
键对应的value
值,还有一个标志位(true
,false
),在查询的时候和遍历的时候是不同的。
goroutine and channel
goroutine
这两个我觉得是比较golang中比较神秘的地方了,也是独有的地方,体现出了golang的高性能,除此之外,接口应该也算一个,这里都不说它了,主要说一下这两个东东。
按我的理解,我觉得学习这两者,要了解操作系统这门知识点,当然如果没学过当然也可以理解,因为没有涉及太深的理论知识。
goroutine
是golang独有的东西,我们可以叫它协程,可能大家都听过进程,学过操作系统的还知道线程,但是协程是真的没有听过。
进程就是你电脑后台正在运行的程序嘛,你可以使用Ctrl + Alt + delete
,打开任务管理器查看。而线程是进程的一个执行实例,一个进程可以包含多个线程从而执行不同的任务。并且我们需要知道:
- 并发=》单核多任务,在一个时间点上,其实只有一个任务在执行
- 并行=》多核多任务,在一个时间点上,有多个任务同时执行
现在一般的电脑都是多核的,也就是具有多个cpu,在golang中可以使用runtime
包进行查看:
package main
import (
"fmt"
"runtime"
)
func main(){
cpuNum := runtime.NumCPU()
fmt.Println("cpuNum=",cpuNum)
//设置使用多少cpu
runtime.GOMAXPROCS(cpuNum - 1)
}
了解了这些,我们就清楚了好大一部分知识点了,离成功就不远了。。。
golang中的协程就是线程上面的分支,是轻量级的线程,可以同时多协程,开启的语句:go function_name
go协程的特点:
- 有独立的栈空间
- 共享程序堆空间
- 调度由用户控制
- 协程是轻量级线程
各一个具体例子:
import (
"fmt"
"strconv"
"time"
)
func test(){
for i := 1; i <= 10; i++{
fmt.Println("hello world!" + strconv.Itoa(i))
time.Sleep(time.Second) //每隔一秒
}
}
func main(){
go test() //开启协程
for i := 1; i <= 100; i++{
fmt.Println("main hello world!" + strconv.Itoa(i))
time.Sleep(time.Second) //每隔一秒
}
}
这样有一个问题就是无法兼顾协程和主线程的运行时间,因为协程是跟随主线程的,如果主线程执行完毕,那么协程将自动关闭。可以通过time.Sleep
来兼容,但是精度不够。
package main
import (
"fmt"
"sync"
"time"
)
var (
mymap = make(map[int]int, 10)
lock sync.Mutex // 全局互斥锁
)
func test(n int){
res := 1
for i :=1; i <= n; i++{
res *= i
}
//加锁
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()
}
多个协程同时开启,对于test
函数的使用发生了阻塞,所以这里提供了一个解决办法,通过lock sync.Mutex 全局互斥锁
,来对test
以及map互斥使用,这样我们就可以顺利运行了。
channel
利用channel可以解决协程之间的通信问题,下面简单介绍:
- 初始化:
1、var intChan chan int
intChan = make(chan int,3)
2、intChan := make(chan int,3)
-
本质
队列,FIFO,先进先出,就类似于是一个管道。 -
遍历
for v := range intChan{}
//channel没有索引,所以只有返回值,而无下标
- 写入,写出
//写入
intChan<-10
//写出
value :=<- intChan
另外提一下代码中的一个知识点:
func line_from_stdin(stdin_chan chan<- string) {}
表明
channel是单向的,只能用来接受数据。
将协程放到管道,多个协程操作同一个管道时不会发生资源竞争问题,就是因为channel的特性。
给一个实际例子:
package main
import "fmt"
func main(){
var intChan chan int
//存放三个int类型的管道
intChan = make(chan int,3)
fmt.Printf("intChan 的值=%d\n",intChan) //存放管道的地址
//向管道写入数据
intChan<- 10
num := 211
intChan<- num
intChan<-50
fmt.Printf("channel len=%v, cap=%v \n",len(intChan),cap(intChan))
//不能超出cap
//从管道中读取数据
var num2 int
num2 = <-intChan
fmt.Println("num2=",num2)
fmt.Printf("channel len=%v, cap=%v \n",len(intChan),cap(intChan))
//在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报错 deadlock
num3 := <-intChan
num4 := <-intChan
fmt.Println("num3,num4=",num3,num4)
fmt.Printf("channel len=%v, cap=%v \n",len(intChan),cap(intChan))
}
select
用法:主要用于监听和channel有关的I/O操作,当I/O操作发生时,触发相应的动作:
- 当管道读到数据时:
case <- chan:
- 当管道写入数据时:
case chan <- 10
default
Json序列化和反序列化
定义:
Json序列化,是将有key-value
结构的数据类型(结构体、map、slice)序列化成Json字符串的操作,反序列化相反。
主要通过encoding/json
包实现,里面包含:
- Marshal
返回值:byte,error
- Unmarshal
返回值:error
两个包用于序列化和反序列化。
结构体序列化的别称:
先给个例子:
type Monster1 struct {
Name string `json:"monster_name"`
Age int `json:"monster_age"`
Birthday string `json:"monster_birthday"`
Sal float64 `json:"monster_sal"`
Skill string `json:"monster_skill"`
}
比如这个结构体序列化的时候会使用其后面的别称。
反序列化时,输入有两个部分:
str
反序列化的字符串,需要转化为byte
,[]byte(str)
- 第二个输入是要转成的类型的引用,比如:
&monster
map中的value为interface
mapint := make(map[string]interface{})
interface{}
代表任何类型,如果要转换类型,则采用:a.(string)