admin管理员组文章数量:1622542
文章目录
- 1. 语言特性
- 1.1 控制结构
- 1.2 常用数据结构
- 1.2.1 常量
- 1.2.2 类型重命名
- 1.2.3 变量
- 1.2.4 匿名变量
- 1.2.5 指针变量
- 1.2.6 数组
- 1.2.7 切片
- 1.2.8 Map
- 1.2.9 结构体和指针 & 反射
- 1.3 函数
- 1.3.1 返回值
- 1.3.2 回调函数
- 1.3.3 闭包(匿名函数)
- 1.4 方法
- 1.5 接口
- 1.6 panic 、defer和recover
- 2. CSP
- 2.1 GMP模型
- 2.2 通道 channel
- 2.3 定时器 Timer
- 2.4 如何停止子协程
最近抽空了解了下Golang这门语言,它是一种由Google开发的编程语言。总结下来,优势是小而简单,编译快,性能与C媲美;也终于清楚了协程的概念。类似的多语言的学习可以巩固一些相似的语言特性,也可以带来新的编程视角。这篇文章主要是对Golang的语言特性做一个系统的梳理,增强记忆。
introduction about Go design from Wiki:
Go is influenced by C (especially the Plan 9 dialect[47][failed verification – see discussion]), but with an emphasis on greater
simplicity and safety. It consists of:
- A syntax and environment adopting patterns more common in dynamic languages:
– Optional concise variable declaration and initialization through type inference (x := 0 instead of int x = 0; or var x = 0;)
– Fast compilation
– Remote package management (go get) and online package documentation- Distinctive approaches to particular problems:
– Built-in concurrency primitives: light-weight processes (goroutines), channels, and the select statement
– An interface system in place of virtual inheritance, and type embedding instead of non-virtual inheritance
– A toolchain that, by default, produces statically linked native binaries without external Go dependencies- A desire to keep the language specification simple enough to hold in a programmer’s head,[52] in part by omitting features that are common in similar languages.
1. 语言特性
1.1 控制结构
if : 可以在表达式前执行一个简单的语句
if v := x - 100; v < 0 {
return v
}
switch : 注意空分支 与 fallthrough的不同,不需要用break来明确退出一个case
for-range: 遍历数组、切片、字符串、Map 等
1.2 常用数据结构
1.2.1 常量
const a = "this is const"
1.2.2 类型重命名
type ServiceType string
const (
ServiceTypeClusterIP ServiceType = "ClusterIP"
ServiceTypeNodePort ServiceType = "NodePort"
ServiceTypeLoadBalance rServiceType = "LoadBalancer"
ServiceTypeExternalName ServiceType = "ExternalName"
)
1.2.3 变量
var 可用于声明一个变量列表,跟函数的参数列表一样,类型在最后;
默认有初始化值
如果初始化时就赋值,则不需要var修饰,需要 “:=” 符号
var b = "this is variable"
var c, b, a bool
var i, j int = 2, 3
//类型转换
var i int = 2
var f float64 = float64(i)
var u uint = uint(f)
//或者
i := 42
f := float64(i)
u := uint(f)
//类型推导
var i int
j := i //j也是一个int
1.2.4 匿名变量
用 _ 来表示,作用就是可以避免创建定义一些无意义的变量,不会分配内存。
1.2.5 指针变量
类型指针和数组切片
作为类型指针时,允许对这个指针类型的数据进行修改指向其它内存地址,传递数据时如果使用指针则无须拷贝数据从而节省内存空间,此外和C语言中的指针不同,Go语言中的类型指针不能进行偏移和运算,因此更为安全。
1.2.6 数组
相同类型且长度固定连续内存片段,以编号访问每个元素
定义方法:var identifier [len]type
var a = [3]int{1, 2, 3} // 声明时初始化
var b = new([3]string) // 通过 new 初始化
var c = make([]string, 3) // 通过 make初始化
1.2.7 切片
切片是对数组一个连续片段的引用,在未初始化前默认为nil, 长度为0
数组定义中不指定长度即为切片
定义方法:var identifier []type
myArray := [5]int{1, 2, 3, 4, 5}
mySlice := myArray[1:3]
fmt.Printf("mySlice %+v\n", mySlice)
fullSlice := myArray[:]
//new返回指针地址
//make返回第一个元素,可预设内存空间,避免未来的内存拷贝
mySlice1 := new([]int) \\&[]
mySlice2 := make([]int, 0) \\[]
mySlice3 := make([]int, 10) \\ [0 0 0 0 0 0 0 0 0 0]
mySlice4 := make([]int, 10, 20)\\ [0 0 0 0 0 0 0 0 0 0]
1.2.8 Map
声明方法:var map1 map[keyType][valueType]
myMap := make(map[string]string, 10)
myMap["a"] = "b"
//函数作为map的value
myFuncMap := map[string]func() int{
"funcA": func() int {
return 1
},
}
fmt.Println(myFuncMap)
f := myFuncMap["funcA"]
fmt.Println(f())
访问map元素
按key取值
value, exists := myMap["a"]
if exists {
println(value)
}
遍历map
for k, v := range myMap {
println(k, v)
}
1.2.9 结构体和指针 & 反射
没有class,类是用结构体来定义的
定义方法: type identifier struct
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type MyType struct {
Name string `json:"name"`
Address
}
type Address struct {
City string `json:"city"`
}
func main() {
mt := MyType{Name: "test", Address: Address{City: "shanghai"}}
b, _ := json.Marshal(&mt)
fmt.Println(string(b))
myType := reflect.TypeOf(mt)
name := myType.Field(0)
tag := name.Tag.Get("json")
println(tag)
tb := TypeB{P2: "p2", TypeA: TypeA{P1: "p1"}}
println(tb.P1)
}
type TypeA struct {
P1 string
}
type TypeB struct {
P2 string
TypeA
}
1.3 函数
main函数 如何传入参数
方法1:
fmt.Println("os args is:", os.Args)
方法2:
name := flag.String("name", "world", "specify the name you want to say hi")
flag.Parse()
init函数
在包初始化时运行,且仅运行一次
1.3.1 返回值
函数可以返回任意数量的返回值
命名返回值,被视作定义在函数顶部的变量
调用者用“占位符_”忽略部分返回值
result, _ = strconv.Atoi(origStr)
go也可以传递变长参数
1.3.2 回调函数
package main
func main() {
func() {
println("hello world")
}()
DoOperation(1, increase)
}
func DoOperation(y int, f func(int, int)) {
f(y, 1)
}
func increase(a, b int) {
println("increase result is:", a+b)
}
1.3.3 闭包(匿名函数)
不能独立存在
可以赋值给其他变量
可以直接调用
func(x,y int){println(x+y)}(1,2)
可以作为函数返回值
func Add()(func(b int) int)
1.4 方法
作用在接收者上的函数
定义方法:func(recv receiver_type) methodName(parameter_list)(return_value_list)
1.5 接口
package main
import "fmt"
type IF interface {
getName() string
}
type Human struct {
firstName, lastName string
}
type Plane struct {
vendor string
model string
}
func (h *Human) getName() string {
return h.firstName + ", " + h.lastName
}
func (p Plane) getName() string {
return fmt.Sprintf("vendor: %s, model: %s", p.vendor, p.model)
}
type Car struct {
factory, model string
}
func (c *Car) getName() string {
return c.factory + "-" + c.model
}
func main() {
interfaces := []IF{}
h := new(Human)//struct初始化意味着空间分配,对struct的引用不会出现空指针
h.firstName = "first"
h.lastName = "last"
interfaces = append(interfaces, h)
c := new(Car)
c.factory = "benz"
c.model = "s"
interfaces = append(interfaces, c)
for _, f := range interfaces {
fmt.Println(f.getName())
}
p := Plane{}
p.vendor = "testVendor"
p.model = "testModel"
fmt.Println(p.getName())
}
1.6 panic 、defer和recover
函数返回前执行某个语句或函数,等同于java中的finally
常见使用场景:关闭资源
package main
import "fmt"
func main() {
defer func() {
fmt.Sprintln("defer func is called")
if err := recover(); err != nil {
fmt.Printf("it is defer func: %+v\n\n", err)
}
}()
panic("a panic is triggered")
}
2. CSP
commmunicating sequential process
描述两个独立的并发实体通过共享通讯channel进行通讯的并发模型
2.1 GMP模型
Goroutine
goroutine不是操作系统线程,而是将一个操作系统线程分段使用,通过调度器实现协作式调度的用户态线程。
每个goroutine都有自己的栈空间,定时器,初始化的栈空间2k左右,空间会随着需求增长
Machine
代表内核线程,记录内核线程信息, M从P上获得goroutine并执行
Process
调度器,负责调度goroutine,维护一个本地goroutine队列
2.2 通道 channel
用于协程之间通讯是同步的。协程之间虽然解耦,但是他们和Channel有着耦合。
- 一端发送数据,一端接受数据
- 同一时间只有一个协程可以访问数据,无共享内存模式可能出现的内存竞争
当缓冲区满,数据发送阻塞
make关键字创建通道可定义缓冲区容量,默认缓冲区容量为0
ch := make(chan int)
go func() {
fmt.Println("hello from goroutine")
ch <- 0 //数据写入Channel
}()
i := <- ch //从Channel中取数据并赋值,读不到数据,会一直阻塞在这里,直到有数据写入到Channel
//下面两种定义的区别
ch1 := make(chan int) //缓冲区大小为0
ch2 := make(chan int, 10)//缓冲区大小为10,向channel中放入的第11个数据会阻塞;
//遍历Channel缓冲区
ch := make(chan int, 10)
go func() {
for i := 0; i < 10; i++ {
rand.Seed(time.Now().UnixNano())
n := rand.Intn(10) //n will be between 0 and 10
fmt.Println("putting:", n)
ch <- n
}
close(ch)
}()
fmt.Println("hello from main")
for v := range ch {
fmt.Println("receiving:", v)
}
单向通道
var c = make(chan int)
go prod(c)
go consume(c)
// 只发送通道
func prod(ch chan<- int) {
for {
ch <- 1
}
}
// 只接收通道
func consume(ch <-chan int) {
for {
<-ch
}
}
关闭通道
通道无需每次关闭,关闭的作用是告诉接收者该通道再无新数据发送,只有发送发需要关闭通道
close(chan)
当多个协程同时运行时,可通过select轮询多个通道
- 若所有通道都阻塞,则等待;定义了default,则执行default;
- 若多个通道就绪,随机选择;
select {
case v := <- ch1:
...
case v := <- ch2:
....
default:
....
}
2.3 定时器 Timer
使用场景: 为协程设定超时时间
ch := make(chan int)
go func() {
//ch <- 1
}()
timer := time.NewTimer(2 * time.Second) //为协程设定超时时间
fmt.Println("put into ch")
select {
case <-ch: //check normal channel
fmt.Println("received from ch")
case <-timer.C:
fmt.Println("timeout waiting from channel ch")
}
2.4 如何停止子协程
方法一:done channel
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("done channel is triggered, exit child go routine")
return
}
}
}()
close(done)
方法二:基于Context
package main
import (
"context"
"fmt"
"time"
)
func main() {
baseCtx := context.Background() //创建context
ctx := context.WithValue(baseCtx, "key_k1", "value_v1") //向context添加键值对
go func(c context.Context) {
fmt.Println(c.Value("key_k1")) //context.Value("key") ==> 通过key获取value;实现了协程间的变量传递
}(ctx)
timeoutCtx, cancel := context.WithTimeout(baseCtx, time.Second) //设置timeout为1秒
defer cancel()
go func(ctx context.Context) {
ticker := time.NewTicker(1 * time.Second) //节拍器,每秒执行一次
for _ = range ticker.C {//每秒做一次for循环
select {
case <-ctx.Done()://context的timeout为一秒,timeout后Done()则有数据
fmt.Println("child process interrupt...")
return
default:
fmt.Println("enter default")
}
}
}(timeoutCtx)
select {
case <-timeoutCtx.Done():
time.Sleep(1 * time.Second)
fmt.Println("main process exit!")
}
}
除此之外,还要注意下述知识的与java的对比理解:
锁的类型及作用
多个协程之间协作
线程 & 协程调度
内存管理
GC
版权声明:本文标题:入门Golang 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1728872638a1177440.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论