new和make的区别,前者返回的是指针,后者返回引用,且make关键字只能创建channel、slice和map这三个引用类型。
如果User结构想实现Test方法,以下写法:func (this *User) Test()
,User的实例和*User都可以调到Test方法,不同的是作为接口时User没有实现Test方法。
interface 作为两个成员实现,一个是类型和一个值,var x interface{} = (*interface{})(nil)
接口指针x不等于nil 。下面一段代码深入展示:
type User struct {
Id int
Name string
Tester
}
type Tester interface {
Test()
}
func (this *User) Test() {
fmt.Println(this)
}
func create() Tester {
var x *User = nil
return x
}
func Test(t *testing.T) {
var x Tester = create()
if x != nil {
t.Log("not nil ")
}
var u *User = x.(*User)
if u == nil {
t.Log("nil ")
}
}
继承通过嵌入实现,也可说go语言没有继承语法。
import关键字前面的"."和"_"的用法,点号表示调用这个包的函数时可以省去包名,下划线表示,纯引入包,因为go语法内没有使用这个包是不能导入的,包引入了,系统会自动调用包的init函数。
select case必须是chan的io操作,为了避免饥饿问题,当多个通道都同时监听到数据时,select机制会随机性选择一个通道读取,一个通道被多个select语句监听时,同理。
关闭通道时所有select 监听都会收到通道关闭信号,某种意义上关闭通道是广播事件。
goroutine的panic如果没有捕获,整个应用程序会crash ,所以安全起见每个复杂的go线都要recover。
在函数退出时,defer的调用顺序是写在后面的先被调用。
init函数在main之前调用,被编译器自动调用,每个包理论上允许有多个init函数,编码上尽量避免同一个包内出现多个init函数。
panic可以中断原有的控制流程,进入一个令人恐慌的流程中,这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,此时程序退出。恐慌可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组.recover的用法,recover可以让进入令人恐慌的流程中的goroutine恢复过来。recover仅在defer函数中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。
Array 和Slice的区别,Array就是一个数据块,值类型而非引用类型,传参时会进行内存拷贝,Slice是个reflect.SliceHeader
结构体。Slice由make函数或者Array[:]创建。
闭包要注意循环调用时,upvalue值一不留意可能只是循环退出的值。如下代码:
func Test(t *testing.T) {
var data int
for i:= 0;i<10;i++{
data ++
go func(){
listen2(data)
}()
}
<- time.After(time.Second)
}
func listen2(data int) {
fmt.Print( data)
}
输出:
26101010101010106
,跟你期望的输出可能不一样。
普通类型向接口类型的转换是隐式的,定义该接口变量直接赋值。接口类型向普通类型转换需要类型断言:value, ok := element.(T)。
Go设计上模糊了堆跟栈的边界,go编译器帮程序员做了对象逃逸分析,优化了内存分配,t := T{}是可以在函数里返回的,并不是像C语言中在栈里分配内存了。
无论以接口或接口指针传递参数,接口指向的值都会被拷贝传递,引用类型(Map/Chan/Slice)拷贝该引用对象,值类型拷贝整个值(string除外)。
go线程的调用时机是由go runtime决定的。
func Test(t *testing.T) {
for i:= 0;i<10;i++{
go listen2(i)
}
<- time.After(time.Second)
}
func listen2(data int) {
fmt.Print( data)
}
输出:
3456781209
调用log.Fatal系列函数后,会再调用 os.Exit(1) 退出程序,Fatal is equivalent to Print() followed by a call to os.Exit(1)
。
如果管道关闭则退出for循环,因为管道关闭不会阻塞导致for进入死循环,如下:
// 错误的做法
func Test_Select_Chan(t *testing.T) {
readerChannel:= make(chan int )
go func(readerChannel chan int ) {
for {
select {
// 判断管道是否关闭
case _, ok := <-readerChannel:
if !ok {
break
}
}
t.Log("for")
}
}(readerChannel)
close(readerChannel)
<- time.After(time.Second*2)
}
// 正确的做法
func Test_Select_Chan1(t *testing.T) {
readerChannel:= make(chan int )
go func(readerChannel chan int ) {
for {
select {
// 判断管道是否关闭
case _, ok := <-readerChannel:
if !ok {
goto BB
//return
}
}
t.Log("for")
}
BB:
}(readerChannel)
close(readerChannel)
<- time.After(time.Second*2)
}
for select 组合不带标签的break语法是跳不出循环,如果要跳出循环,要设置goto 标签或者直接return返回。
map,slice,array,chan
的数据存取值类型数据都是值拷贝赋值,这个跟很多脚本语言不同,一定要注意:var list []mydata
var hash map[string]mydata
type mydata struct {
A int
}
func Test(t *testing.T) {
list = make([]mydata, 1)
data := list[0]
data.A = 10
hash = make(map[string]mydata)
hash["test"] = mydata{}
data = hash["test"]
data.A = 10
t.Log(list[0].A, hash["test"].A)
}
输出:
0 0
type CMD struct{
Cmd string `json:"cmd"`
Data Data `json:"data"`
UserId string `json:"userId"`
}
包的循环引用编译错误,解决方法:提取公共部分到独立的包或者定义接口依赖注入。
return XXX
不是一条原子指令:
func Test(t *testing.T) {
t.Log(test())
t.Log(test1())
}
func test() (result int) {
defer func() {
result++
}()
return 1
}
func test1() (result int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
输出:
2
5
return XXX
不是一条原子指令,函数返回过程是,先对返回值赋值,再调用defer函数,然后返回调用函数所以test方法的return 1
可以拆分为:
result = 1
func()(result int){
result ++
}()
return
list := []int{12, 1242, 35, 23, 534, 23, 1}
listNew := make([]int, 1, len(list))
copy(listNew, list)
for i := 0; i < len(listNew); i++ {
fmt.Println(listNew[i])
}
输出:
12
list := []int{12, 1242, 35, 23, 534, 23, 1}
listNew := list[:3]
listNew2 := list[:5]
listNew[1] = 999
fmt.Println(listNew2[1])
输出:
999
编译时设置编译参数去掉调试信息,可以让生成体积更小:go build -o target.exe -ldflags "-w -s" source.go
Go语言里,string字符串类是不可变值类型,字符串的"+"连接操作、字符串和字符数组之间的转换string([]byte) 都会生成新的内存存放新字符串,当要对字符串频繁操作时做好先转换成字符数组。但是字符串作为参数传参时,此处go编译器作了优化,不会导致内存拷贝,引用的是同一块内存。benchmark如下:
func Benchmark_aa(b *testing.B) {
very_long_string:= ""
for i := 0; i < b.N; i++ {
very_long_string += "test " + "and test "
}
}
func Benchmark_bb(b *testing.B) {
very_long_string:= []byte{}
for i := 0; i < b.N; i++ {
very_long_string = append(very_long_string,[]byte("test ")...)
very_long_string = append(very_long_string,[]byte("and test ")...)
}
}
输出:
200000 135817 ns/op
50000000 39.3 ns/op
Go build/run/test 有个参数 -race ,设置-race运行时会进行数据竞态检测,并把关键代码打印输出,不过数据竞态不能全依赖race检测,不一定能全部检测出来。
如果非必要必要使用反射reflect和unsafe包内的函数,一定要使用时,要用runtime.KeepAlive函数告知SSA编译器在指定的代码段内不要回收该内存块。
不要打印整个map对象或者对象里有嵌套map的对象,打印函数会不加锁遍历map的每个元素,如果此时外部刚好有方法对map进行写操作,map就进入并发读写,runtime会panic。
注意range 循环迭代时key 的地址,for k,v:=range list 其中k 在迭代时指向同一个地址。
append追加切片用法:
type S struct {
}
func (this S)String() string{
return fmt.Sprint( "S struct: ",this )
}
func Test_print(t *testing.T) {
var s S
t.Log(s.String())
}
34.循环语句里正整型迭代值边界问题,迭代值边界递减到负值,下面的代码会进入死循环:
for i:= uint8(10);i>=0;i--{
t.Log(i)
}
recover只处理本goroutine调用栈,goroutine的panic如果没有捕获,整个应用程序会crash ,所以安全起见每个复杂的go线都要recover。
map 并发读写的错误无法用panic捕获。
对于小对象,直接将值类型的对象交由 map 保存,远比用该对象指针高效。这不但减少了堆内存分配,关键还在于垃圾回收器不会扫描非指针类型 key/value 对象。
Go 使用 channel 实现 CSP 模型。处理双方仅关注通道和数据本身,无需理会对方身份和数量,以此实现结构性解耦。在各文宣中都有 “Don't communicate by sharing memory, share memory by communicating.” 这类说法。但这并非鼓励我们不分场合,教条地使用 channel。在我看来,channel 多数时候适用于结构层面,而非单个区域的数据处理。原话中 “communicate” 本就表明一种 “message-passing”,而非 “lock-free”。所以,它并非用来取代 mutex,各自有不同的使用场景。
变量逃逸和函数内联状态分析 go build -gcflags "-m" -o main.exe main.go
。
go汇编指令 go build -gcflags "-N -l" -o main.exe main.go && go tool objdump -s "main\.main" main.exe
关闭内联优化:go build -gcflags "-N -l"
。
关于defer机制,编译器通过 runtime.deferproc
“注册” 延迟调用,除目标函数地址外,还会复制相关参数(包括 receiver)。在函数返回前,执行 runtime.deferreturn
提取相关信息执行延迟调用。这其中的代价自然不是普通函数调用一条 CALL 指令所能比拟的,单个函数里过多的 defer 调用可尝试合并。最起码,在并发竞争激烈时,mutex.Unlock
不应该使用 defer,而应尽快执行,仅保护最短的代码片段。
对map预设容量,map会按需扩张,但须付出数据拷贝和重新哈希成本。如有可能,应尽可能预设足够容量空间,避免此类行为发生。
go语言调用c语言:
for j := 0; j < len(array); j++ {
if INDEX == j {
array = append(array[:j], array[j+1:]...)
break
}
}
for j :=INDEX; j < len(array)-1; j++ {
array[j] = array[j+1]
}
array = array[:len(array)-1]
eBook分享大集合 主要以 IT 领域经典书籍收藏,以备不时之需,欢迎各位补充,请留言。 服务器系统类(9) Linux 高性能服务器编程 Shell 脚本学习指南 高级 Bash 脚本编程指南.3.9.1 (杨春敏 黄毅 译) 鸟哥的 Linux 私房菜基础篇(第3版) 深入理解计算机系统 计算机组成原理(白中英) 计算机操作系统(第3版.汤子瀛) 计算机组成原理(第2版.唐朔飞) 汇编语言(
ebook 有兴趣了解更多吗? 然后,您应该在此处下载相关的电子书。 Java中的大多数性能问题都可归因于少数根本原因。 当然,偶尔会有一些奇怪的极端情况突然出现,并在应用程序中造成严重破坏,但是在大多数情况下,Java中的性能问题都是相当容易解决的问题。 在这本电子书中,AppDynamics的史蒂夫·海恩斯(Steve Haines)讨论了他在性能分析师期间遇到的一些最常见的问题,以及如何识别
ebook 电子书或电子书已经存在很长时间了,但是阅读电子书的便捷设备是相对较新的发展。 在手机,平板电脑和专用电子书阅读器之间,您生活中的某些设备很可能会用来阅读电子书。 这对于提高您的阅读量非常有用,但这引出了一个问题,即电子书有哪些开放文件格式,哪些是最好的。 为什么选择电子书? 电子书很棒。 我使用它们的时间比专门读者要长得多。 我很欣赏电子书使我能够随身携带多本文本,而不必承担数张纸的重
Redux 文档旨在教授 Redux 的基本概念,并解释在实际应用程序中使用的关键概念。但是,文档无法涵盖所有内容。令人高兴的是,还有许多其他很好的资源可用于学习 Redux。我们鼓励你仔细查看一下。 其中许多内容涵盖了超出文档范围的主题 , 或以可能更适合您学习方式的方法阐述相同的内容。 此页面包含我们对可用于学习 Redux 的一些最佳外部资源的建议。有关 React,Redux,Javasc
学习资源 有很多社区资源可以帮助你开发应用。如果你对Meteor感兴趣,希望你能参与其中! 教程 快速开始Meteor 官方教程! Stack Overflow 对于技术问题,提问、寻找答案最好的去处就是 Stack Overflow. 确保给你的问题添加 meteor 标签。 论坛 访问 Meteor discussion forums宣布项目,寻求帮助,讨论社区或是讨论核心模块的变动。 Git
学习资源 Deep learning book Deep learning resources 以及 tutorial cs231n cs224d Papers Moning Paper colah’s blog kdnuggets MachineLearning-Handbook arXiv arXiv Sanity Neural Networks and Deep Learning UFLDL
官方资源 本文档就是最好的入门学习资源,其它一些有用的资源如下: Cocos官网 中文社区 引擎官方测试项目 测试项目的编译和运行在 环境搭建章节 介绍 网络资源 zilongshanren.com/categories/Cocos2d-x/ learnopengl-cn.github.io/ github.com/fusijie/Cocos-Resource
书籍 RxSwift - By Raywenderlich 视频 Learning Path: RxSwift from Start to Finish - By Realm 团队 RxSwift in Practice - By Raywenderlich Reactive Programming with RxSwift - By RxSwift 图书的核心作者 Boxue.io RxSwif
近些年来出现了大量的关于 Git 的文档,教程和文章。我建议你浏览一下这些在线资源: 命令速查表 "Git - the Simple Guide" "Pro Git" ebook