学习go遇到的一些问题和概念汇总
go get安装x/sys失败的解决
// $GOPATH这段地址请自行修改,下载到goroot和gopath对应位置即可,不同系统略有不同
git clone https://github.com/golang/net.git $GOPATH/src/github.com/golang/net
git clone https://github.com/golang/sys.git $GOPATH/src/github.com/golang/sys
git clone https://github.com/golang/tools.git $GOPATH/src/github.com/golang/tools
ln -s $GOPATH/src/github.com/golang $GOPATH/src/golang.org/x
go get命令安装golang.org比较慢的话,一般都可以在github.com找到对应的package,下载后放到对应的目录即可
go的环境变量设置
在/etc/profile最下面加入以下代码:
# 可自行改成你的目录
export GO_INSTALL_DIR=$HOME
export GOROOT=$GO_INSTALL_DIR/go
export GOPATH=$HOME/mygo
export PATH=$GOPATH/bin:$PATH:$GO_INSTALL_DIR/go/bin
import
import _ 执行init函数,但不需要包所有函数
import . 解决循环依赖问题
&和*
&符号的意思是对变量取地址,如:变量a的地址是&a
符号的意思是对指针取值,和聽&聽可以互相抵消,同时注意,*&可以抵消掉,但&*是不可以抵消的
切片容量
容量当做成总长度减去左指针走过的元素值
一旦一个切片无法容纳更多的元素,Go语言就会想办法扩容。但它并不会改变原来的切片,而是会生成一个容量更大的切片,然后将把原有的元素和新元素一并拷贝到新切片中。在一般的情况下,你可以简单地认为新切片的容量(以下简称新容量)将会是原切片容量(以下简称原容量)的2倍。
但是,当原切片的长度(以下简称原长度)大于或等于1024时,Go语言将会以原容量的1.25倍作为新容量的基准(以下新容量基准)。新容量基准会被调整(不断地与1.25相乘),直到结果不小于原长度与要追加的元素数量之和(以下简称新长度)。最终,新容量往往会比新长度大一些,当然,相等也是可能的。
另外,如果我们一次追加的元素过多,以至于使新长度比原容量的2倍还要大,那么新容量就会以新长度为基准。
大小写
在Go语言中,没有特别的关键字来声明一个方法、函数或者类型是否为公开的,Go语言提供的是以大小写的方式进行区分的,如果一个类型的名字是以大写开头,那么其他包就可以访问;如果以小写开头,其他包就不能访问。
go的并发
概念 | 说明 |
---|---|
进程 | 一个程序对应一个独立程序空间 |
线程 | 一个执行空间,一个进程可以有多个线程 |
逻辑处理器 | 执行创建的goroutine,绑定一个线程 |
调度器 | Go运行时中的,分配goroutine给不同的逻辑处理器 |
全局运行队列 | 所有刚创建的goroutine都会放到这里 |
本地运行队列 | 逻辑处理器的goroutine队列 |
并发的概念和并行不一样,并行指的是在不同的物理处理器上同时执行不同的代码片段,并行可以同时做很多事情,而并发是同时管理很多事情,因为操作系统和硬件的总资源比较少,所以并发的效果要比并行好的多,使用较少的资源做更多的事情,也是Go语言提倡的。
通道
h:=make(chan int)
通道类型和Map这些类型一样,可以使用内置的make函数声明初始化,这里我们初始化了一个chan int类型的通道,所以我们只能往这个通道里发送int类型的数据,当然接收也只能是int类型的数据。
我们知道,通道是用于在goroutine之间通信的,它具有发送和接收两个操作,而且这两个操作的运算符都是<-。
ch <- 2 //发送数值2给这个通道
x:=<-ch //从通道里读取值,并把读取的值赋值给x变量
<-ch //从通道里读取值,然后忽略
看例子,慢慢理解发送和接收的用法。发送操作<-在通道的后面,看箭头方向,表示把数值2发送到通道ch里;接收操作<-在通道的前面,而且是一个一元操作符,看箭头方向,表示从通道ch里读取数据。读取的数据可以赋值给一个变量,也可以忽略。
通道我们还可以使用内置的close函数关闭。
close(ch)
无缓冲的通道
无缓冲的通道指的是通道的大小为0,也就是说,这种类型的通道在接收前没有能力保存任何值,它要求发送goroutine和接收goroutine同时准备好,才可以完成发送和接收操作。
从上面无缓冲的通道定义来看,发送goroutine和接收gouroutine必须是同步的,同时准备后,如果没有同时准备好的话,先执行的操作就会阻塞等待,直到另一个相对应的操作准备好为止。这种无缓冲的通道我们也称之为同步通道。
func main() {
ch := make(chan int)
go func() {
var sum int = 0
for i := 0; i < 10; i++ {
sum += i
}
ch <- sum
}()
fmt.Println(<-ch)
}
有缓冲的通道
有缓冲通道,其实是一个队列,这个队列的最大容量就是我们使用make函数创建通道时,通过第二个参数指定的。
ch := make(chan int, 3)
这里创建容量为3的,有缓冲的通道。对于有缓冲的通道,向其发送操作就是向队列的尾部插入元素,接收操作则是从队列的头部删除元素,并返回这个刚刚删除的元素。
当队列满的时候,发送操作会阻塞;当队列空的时候,接受操作会阻塞。有缓冲的通道,不要求发送和接收操作时同步的,相反可以解耦发送和接收操作。
想知道通道的容量以及里面有几个元素数据怎么办?其实和map一样,使用cap和len函数就可以了。
cap(ch)
len(ch)
cap函数返回通道的最大容量,len函数返回现在通道里有几个元素。
func mirroredQuery() string {
responses := make(chan string, 3)
go func() { responses <- request("asia.gopl.io") }()
go func() { responses <- request("europe.gopl.io") }()
go func() { responses <- request("americas.gopl.io") }()
return <-responses // return the quickest response
}
func request(hostname string) (response string) { /* ... */ }
单向通道
有时候,我们有一些特殊场景,比如限制一个通道只可以接收,但是不能发送;有时候限制一个通道只能发送,但是不能接收,这种通道我们称为单向通道。这个在生产者消费者很实用。
定义单向通道也很简单,只需要在定义的时候,带上<-即可。
var send chan<- int //只能发送
var receive <-chan int //只能接收