社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
Go_入坑笔记
下载及其文档 : https://golang.org/doc/install?download=go1.10.3.windows-amd64.zip
vscode 配置 go 及其 下载失败解决办法: https://blog.csdn.net/Yo_oYgo/article/details/79065966
debugger : https://zhuanlan.zhihu.com/p/26473355
菜鸟教程: http://www.runoob.com/go/go-environment.html
参考:
通过go build加上要编译的Go源文件名,我们即可得到一个可执行文件,默认情况下这个文件的名字为源文件名字去掉.go后缀。
$ go build hellogo.go
$ ls
hellogo* hellogo.go
当然我们也 可以通过-o选项来指定其他名字:
$ go build -o myfirstgo hellogo.go
$ ls
myfirstgo* hellogo.go
如果我们在go-examples目录下直接执行go build命令,后面不带文件名,我们将得到一个与目录名同名的可执行文件:
$ go build
$ ls
go-examples* hellogo.go
与build命令相比,install命令在编译源码后还会将可执行文件或库文件安装到约定的目录下。
同一个目录下不能存在不同包名的文件
import 别的包规则
package main
import (
pkg001 "GoLab/test_pkg/pkg001" // 重命名别名为 pkg001 在本文件中的使用, 一般不要这样干
_ "GoLab/test_pkg/pkg002" // _ 防止 未被使用的包, 被格式化代码时被编辑器自动干掉这一行
"fmt" // 导入内置包
)
import 的流程. 参考: https://blog.csdn.net/zhangzhebjut/article/details/25564457
程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:
func init() { // 是保留的内置方法, import时自动执行
fmt.Println("--- init")
}
empty := struct{}{}
println("empty len:", unsafe.Sizeof(empty)) // 0, 空结构体的长度为 0, 常用于 map里面做value值, 因为go里面集合没有set, 所以用map变相做set
golang分配内存有一个make函数,该函数第一个参数是类型,第二个参数是分配的空间,第三个参数是预留分配空间. 例如a:=make([]int, 5, 10), len(a)输出结果是5,cap(a)输出结果是10,然后对a[4]进行赋值发现是可以得,但对a[5]进行赋值发现报错了,于是郁闷这个预留分配的空间要怎么使用呢,于是google了一下发现原来预留的空间需要重新切片才可以使用,于是做一下记录,代码如下。
func main(){
a := make([]int, 10, 20)
fmt.Printf("%d, %dn", len(a), cap(a))
fmt.Println(a)
b := a[:cap(a)]
fmt.Println(b)
}
/*
10, 20
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
*/
make()分配:内部函数 make(T, args) 的服务目的和 new(T) 不同。
它只生成切片,映射和程道,并返回一个初始化的(不是零)的,type T的,不是 *T 的值。
这种区分的原因是,这三种类型的数据结构必须在使用前初始化.
比如切片是一个三项的描述符,包含数据指针(数组内),长度,和容量;在这些项初始化前,切片为 nil 。
对于切片、映射和程道,make初始化内部数据结构,并准备要用的值。
记住 make() 只用于 映射、切片、程道,不返回指针。要明确的得到指针用 new() 分配。
go 关键字用来创建 goroutine (协程),是实现并发的关键。go 关键字的用法如下:
//go 关键字放在方法调用前新建一个 goroutine 并让他执行方法体
go GetThingDone(param1, param2);
//上例的变种,新建一个匿名方法并执行
go func(param1, param2) {
}(val1, val2)
//直接新建一个 goroutine 并在 goroutine 中执行代码块
go {
//do someting...
}
因为 goroutine 在多核 cpu 环境下是并行的。如果代码块在多个 goroutine 中执行,我们就实现了代码并行。那么问题来了,怎么拿到并行的结果呢?这就得用 channel 了。
//resultChan 是一个 int 类型的 channel。类似一个信封,里面放的是 int 类型的值。
var resultChan chan int
//将 123 放到这个信封里面,供别人从信封中取用
resultChan <- 123
//从 resultChan 中取值。这个时候 result := 123
result := <- resultChan
chan 是信号的关键字, 作用有点像c++多线程里面的 signal, 发送信号给其他线程, 通知其可以继续往下跑了.
在 go 里 chan 的使用是结合了 go,select 实现了 goroutine, 多并发.
func test_chan03() {
c1 := make(chan string) // 声明 信号
c2 := make(chan string)
go func() { // go 关键字, 新建一个协程跑这个方法
time.Sleep(time.Second * 1)
c1 <- "one" // 往 c1 信号中丢数据, 也就是通知 c1 阻塞的地方可以继续跑了
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1: // 阻塞, 等待 c1 信号通知, 如果收到通知, 这跑这个case, 并把数据丢该 msg1
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
fmt.Println("程序结束666")
/*
msg1 := <-c1 表示 阻塞, 等待c1信号通知, 收到通知后把数据 赋值给 msg1. 如果不需要信号中的数据, 可以可以这样写 <-c1
c1 <- "one" 表示 通知 c1 信号阻塞的地方可以继续运行了, 并往里面丢了一个数据 "one"
*/
}
chan 在函数中定义形参是时可以指定是 读写,只读,只写 三个形式, 作用与 c++ 中 const关键字 差不多
fnRW := func(c chan int) { // c可以读写
c <- 6
val := <-c
fmt.Println("val:", val)
}
fnR := func(c <-chan int) { // c只读
// c <- 6 // 报错: send to receive-only type <-chan int
val := <-c
fmt.Println("val:", val)
}
fnW := func(c chan<- int) { // c只写
// <-c // 报错: receive from send-only type chan<- int
c <- 6
}
参考: https://www.cnblogs.com/baiyuxiong/p/4545028.html
defer 的思想类似于C++中的析构函数,不过Go语言中“析构”的不是对象,而是函数,defer就是用来添加函数结束时执行的语句。注意这里强调的是添加,而不是指定,因为不同于C++中的析构函数是静态的,Go中的defer是动态的
defer 中使用匿名函数依然是一个闭包。
func test_defer() {
x, y := 1, 2
defer func(a int) {
fmt.Printf("x:%d,y:%dn", a, y) // y 为闭包引用
}(x) // 复制 x 的值
x += 100
y += 100
fmt.Println(x, y)
}
defer 还有一个重要的作用是用于 panic 时的 恢复, panic 恢复也只能在 defer 中.
参考: http://wiki.jikexueyuan.com/project/the-way-to-go/13.3.html
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:
result, err:= Sqrt(-1)
if err != nil {
fmt.Println(err)
}
// https://talks.golang.org/2012/concurrency.slide#32
select {
case v1 := <-c1:
fmt.Printf("received %v from c1n", v1)
case v2 := <-c2:
fmt.Printf("received %v from c2n", v1)
case c3 <- 23:
fmt.Printf("sent %v to c3n", 23)
default:
fmt.Printf("no one was ready to communicaten")
}
上面这段代码中,select 语句有四个 case 子语句,前两个是 receive 操作,第三个是 send 操作,最后一个是默认操作。代码执行到 select 时,case 语句会按照源代码的顺序被评估,且只评估一次,评估的结果会出现下面这几种情况:
以下描述了 select 语句的语法:
每个case都必须是一个通信
所有channel表达式都会被求值
所有被发送的表达式都会被求值
如果任意某个通信可以进行,它就执行;其他被忽略。
如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
否则:
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
...
method_namen [return_type]
}
/* 定义结构体 */
type struct_name struct {
/* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
/* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
/* 方法实现*/
}
参考: https://blog.csdn.net/wangshubo1989/article/details/72478014
报错: 语法没有指定
-> 在 test.proto 指定语法
syntax = "proto2";
package goprotobuf;
报错: –go_out: protoc-gen-go
-> 需要将 go get -u github.com/golang/protobuf/protoc-gen-go
下载的 protoc-gen-go.exe 所在目录加入环境变量
容器内默认工作区是 /go
, 所以可以挂载到 /go/src
目录下
ssh 远程连进去, 不知道为啥 没有go指令, 需要自己添加到环境变量中 .bash_profile
export PATH=$PATH:/usr/local/go/bin
GO_BIN=/usr/local/go/bin
export GO_BIN
然后使其生效 # source .bash_profile
下载 go1.10.3.linux-amd64.tar , 地址:https://golang.google.cn/dl/
解压: # tar zxvf go1.10.3.linux-amd64.tar.gz -C /usr/local
增加环境变量
# vi ~/.bash_profile
...
export GOROOT=/usr/local/go
export GOPATH=/mytemp/GoLab # 项目地址
export PATH=$PATH:$GOPATH:/usr/local/go/bin
# source ~/.bash_profile # 使其生效
查看命名, ok
# go version
go version go1.10.3 linux/amd64
Q: can’t load package: package test: found packages main (base.go) and testgo (test_go.go) in E:GoLabsrctest
A: 同一个目录下不能存在不同包名的文件
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!