前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang笔记

Golang笔记

作者头像
春哥大魔王
发布2018-04-17 17:51:33
7000
发布2018-04-17 17:51:33
举报

静态编译

编译时一个将源代码翻译成低级语言的过程。编译过程比较慢,在设计Go时,编译速度是主要的设计目标之一。静态类型意味着变量必须指定一个类型,如整形,字符串,布尔,数组等,可以在声明变量时指定变量类型,大多数情况下,让编译器自动去推断变量类型。

垃圾回收

变量有一个确定的生命周期。例如函数中定义的局部变量,当函数退出时变量就不存在了。语言的垃圾回收机制可以记录不在使用的变量,然后释放他们占用的内存。垃圾回收机制带来一些性能影响。

代码运行

go run命令会先编译然后再运行你的代码,会在一个临时目录下编译这段代码,然后执行,最后自动清除生成的临时文件。如果只是编译代码可以使用go build。

变量赋值

第一种方式:

代码语言:javascript
复制
var power intpower = 9000

第二种方式:

代码语言:javascript
复制
var power int = 9000

第三种方式:用于声明一个变量并给变量赋值,go可以推断变量类型,在第一次声明变量时,使用:=,此时确定了变量类型。但随后对于此变量的赋值,使用=。

代码语言:javascript
复制
power := 9000gg := getPower()func getPower() int{    return 9001}

第四种方式:go支持多个变量同事赋值

代码语言:javascript
复制
name, power := "Goku", 9000

函数声明

函数支持多值返回

没有返回值:

代码语言:javascript
复制
func log(message string){
}

一个返回值:

代码语言:javascript
复制
func add (a int, b int) int{
}

两个返回值: func power(name string)(int,bool){ } 多个返回值的场景使用比较多,如果只想获得返回值中的某个值,可以将另一个返回赋值给_:

代码语言:javascript
复制
_, exists:=power("goku")if exists == false{
}

_是一个空白标识符,多用在返回值时没有真正的赋值,无论返回值是什么类型。

如果函数的参数都是相同的类型,可以简洁的定义:

代码语言:javascript
复制
func add(a,b int) int{
}

结构体

go 不像面向对象语言,没有对象和继承的概念。因此也没有很多面向对象的语言的特征比如多态和重载。 go提供了结构体,如:

代码语言:javascript
复制
type Sanya struct{    Name string
    Province int}

通过简单的方式创建一个结构体值类型:

代码语言:javascript
复制
goku := Sanya{    Name : "sanya",    Province :23,
}

注意上面结构体结尾的逗号是不能省的。 当不需要给结构体设置任何值甚至任何字段:

代码语言:javascript
复制
goku := Sanya{}goku := Sanya{Name:"sanya"}
goku.Province = 23

也可以省略字段的名字:

代码语言:javascript
复制
goku := Sanya{"sanya",23}

大多数情况,我们不希望变量直接关联一个值,而是希望一个指针指向变量的值,因为在go语言中,函数的参数传递都是按拷贝传递。指针是一个内存地址。通过指针可以找到这个变量的实际的值,是一种间接的取值。

代码语言:javascript
复制
func main(){
    goku := &Sanya{"sanya",9000}
    Super(goku)
    fm.Println(goku.Power)
}func Super(s *Sanya){
    s.Power = 10000}

结果是10000,这样就是传递了指针。复制一个指针变量的开销比复制一个复制复杂的结构体小。

构造函数

结构体没有构造函数,你可以创建一个函数返回一个相应类型的实例来代替:

代码语言:javascript
复制
func NewSanya(name string, province int) Sanya{    return Sanya{
        Name:name,
        Province:province,
    }
}

为新创建的对象分配内存:

代码语言:javascript
复制
goku := &Sanya{    name:"goku",    province:23}

对已定义对结构体进行扩展:

代码语言:javascript
复制
type Sanya struct{    Name string
    Province int
    Father *Sanya}

初始化:

代码语言:javascript
复制
gohan := &Sanya{    Name:"Sanya",    Province:23,    Father:&Sanya{        Name:"Haiko",        Province:23,        Father:nil,
    }
}

指针类型和值类型

当你写go代码时,很自然就会问自己,这里应该使用值类型还是指针类型。如果你不确定时,就使用指针。值传递是一种确保数据不可变对方法。有时候需要函数内对调用代码进行改变,需要使用指针。 即使你不打算改变数据,也要考虑大结构体拷贝的开销,如果小的结构体可以进行拷贝。

数组

数组是固定大小的。声明数组时必须指定他们的大小,一旦数组大小被指定,他就不能扩展变大。

代码语言:javascript
复制
var scores [10]intscores[0] = 300// 直接初始化一个有值的数组scores := [4]int{9001,9002,9003,9004}// 遍历数组for index,value:= range scores{
}

数组效率高但是不灵活,我们处理数据时,一般不知道元素的数量,因此使用切片。

切片

在go中你一般很少使用数组。会更多使用切片。切片是一个轻量级的结构体封装,这个结构体被封装后,代表一个数组的一部分。 创建切片时和创建数组不同的是,不需要指定大小。

代码语言:javascript
复制
scores := []int{1,2,3,4}

scores := make([]int,0,10) //长度为0但是容量为10的分片scores := append(scores,5)

哈希表

定义键值对,可以通过make创建:

代码语言:javascript
复制
lookup := make(map[stirng]int)
lookup["goku"] = 9001

包管理

如果你已经装来git,执行如下命令:

go get github.com/mattn/go-sqlite3 go get将得到这些远程文件并将他们保存在你的工作空间。导入包到工作空间:

代码语言:javascript
复制
import(    "github.com/mattn/go-sqlite3")

接口

接口是一种类型,他只定义了声明,没有具体实现。如:

代码语言:javascript
复制
type Logger interface{    Log(message string)
}

接口可以在代码中实现解耦。

Go中Buffer高效拼接字符串及自定义线程安全Buffer:

Go中可以使用“+”合并字符串,但这种方式效率非常低,每合并一次,都创建一个新的字符串,就必须遍历复制一次字符串。可以通过Buffer高效拼接字符串。 使用bytes.Buffer来组装字符串,不需要复制,只需要将添加字符串放在缓存末尾即可。由于Buffer中的write和read函数中都未发现锁的踪影,所以Buffer的并非是不安全的。

Go特有的并发编程模型方式:

代码语言:javascript
复制
Goroutine & Channel;

协程Goroutine

在Go世界里,每一个并发执行的活动称为goroutine。 通过goroutine,可以实现并行运算,十分便捷。 go协程类似于一个线程,但是协程由go自身调度,不是系统。在协程中对代码可以和其他代码并发执行。

代码语言:javascript
复制
func main(){    fmt.Println("start")    go process()    time.Sleep(time.Millisecond * 10)    fmt.Println("done")
}func process(){
    fmt.Println("processing")
}

我们如何启动一个协程对。只是简单对将go关键字附在要执行对函数前面即可。 go协程很容易创建且开销极小。最终多个go协程将会在同一个底层系统线程上运行。这也是常称为M:N线程模型,因为我们有M个应用协程运行在N个系统线程上。结果就是,一个go协程对开销和系统线程比起来相对低(一般都是几十K)。在现代硬件上,可以跑成千上万对协程。 还隐藏了映射和调度的复杂性。并发执行让go自己去处理。主线程在退出前不会等待所有的协程执行完毕,所以主线程在退出前,协程才有机会执行,所以我们必须让代码协同。

Go高并发Http请求

目标:能够处理从上百万个端点发来的大量POST请求。HTTP请求处理函数会收到包含很多payloads的JSON文档。这些payloads需要被写到Amazon S3上,接着有map-reduce系统来处理。

我们通常会将请求放入队列,通过一定数量(例如通过核心CPU数)goroutine组成一个worker pool,worker pool中的worker读取队列执行任务,最理想的情况下,CPU的所有核都会并行的执行任务。

然后设置两个集群,一个用作处理HTTP请求,一个用作workers。这样可以根据处理后台的工作量进行扩容。

主Goroutine做了什么?

  • 启动系统检测器;
  • 设定通用配置,检查运行环境;
  • 创建定时垃圾回收器;
  • 执行main包的init函数;
  • 执行main包的main函数;
  • 进行一些善后处理工作;

同步

创建一个协程没有难度,启动很多协程开销也不大。但是并发执行的代码需要协同。为了解决这个问题,go提供了管道(channels)。 协程会将代码函数拆分为很多汇编指令,在并发场景下,如果想安全的操作一个变量,唯一的手段就是读取该变量。可以任意多的读,但写必须同步。可以依赖于cpu架构的真正原子操作。更多时候使用一个互斥锁。

代码语言:javascript
复制
//定义锁lock sync.Mutex//使用锁lock.Lock()//开锁defer lock.Unlock()

Channel

并发编程的挑战在于数据共享。如果你的go协程没有共享数据,就不需要担心她们。但是现实场景中常常需要多个请求共享数据。通道用于go协程之间传递数据,go协程可以通过通道,传递数据到另一个go协程。结果就是任何时候只有一个go协程可以访问数据。

  • 即通道类型,Go的预定义类型之一。
  • 类型化,并发安全的通用型管道。
  • 用于在多个Goroutine之间传递数据。
  • 以通讯的方式共享内存的最直接体现。

Channel的Happens before原则:

发送操作开始->值拷贝(产生副本)->发送操作结束->接收操作开始->接收方持有值->接收操作结束。 Channel可以协调多个Goroutine的运行。

通道也有类型,就是将要在通道传递到数据的类型,如创建一个通道,这个通道可以用来传递一个整数:

代码语言:javascript
复制
c := make(chan int)// 将这个通道传递给一个函数fun worker(c chan int){
}//通道发送数据CHANNEL <- DATA//通道接收数据VAR := <-CHANNEL

尖头指向的方向是数据的流动方向。 当我们从一个通道接收或向通道发送数据时会阻塞,直到有数据。

定义一个数据处理者结构体:

代码语言:javascript
复制
type Worker struct{
    id int
}fun (w Worker) process(c chan int){    for{        data := <-c
        fat.Pringtf("worker data",w.id)
    }
}

我们的worker很简单,会一直等待数据,直到数据可用,然后处理它,他在一个循环中,永远尽职的等待更多的数据并处理。

启动多个worker:

代码语言:javascript
复制
c := make(chan int)for i:=0; I<4; I++{
    worker := Worker{id:i}    go worker.process(c)
}

创建一些任务:

代码语言:javascript
复制
for{
    c <- rand.Int()    time.Sleep(time.Millisecond*50)
}

我们不知道哪个worker将获得数据。但go可以确保往一个通道发送数据时,仅一个单独的接收器可以接收。通道提供了所有的同步代码。

© 著作权归作者所有

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 春哥talk 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 静态编译
  • 垃圾回收
  • 代码运行
  • 变量赋值
  • 函数声明
  • 结构体
  • 构造函数
  • 指针类型和值类型
  • 数组
  • 切片
  • 哈希表
  • 包管理
  • 接口
  • 协程Goroutine
  • Go高并发Http请求
  • 同步
  • Channel
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档