1、描述javascript
go语言是直接将源码编译成二进制机器码的语言;它支持面向对象、也支持函数式编程;go是强类型语言;Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发。java
2、基本语法python
一、切片与数组差异:切片是动态化数组,切片是注意二者初始化和函数的区别git
a.初始化差异golang
数组须要指定大小,不指定也会根据初始化的自动推算出大小,不可改变 编程
a:=[...] int {1,2,3} ; a:=[3] int {1,2,3}数组
切片不须要指定大小浏览器
a:=[] int {1,2,3}; a:=make([]int,3); a:=make([] int ,3,5)缓存
b.函数传递:并发
数组须要明确指定大小,数组是值传递,不一样的数组老是表明不一样的存储。
切片不须要明确指定大小,切片是地址传递。多个切片若是表示同一个数组的片断,它们能够共享数据;所以一个切片和相关数组的其余切片是共享存储的。
func changeArray(a [3]int) {//数组
a[0] = 100
}
func changeSlice(s []int) {//切片
s[0] = 100
}
func main(){
a := [...]int{1, 2, 3}
changeArray(a)
fmt.Println(a[0]) //值传递,输出结果:1
var s []int = []int{1, 2, 3, 4}
fmt.Println(len(s), cap((s)))
s = append(s, 6, 7, 8)
fmt.Println(len(s), cap(s))
changeSlice(s)
fmt.Println(s[0]) //地址传递,输出结果:100
}
c.切片和垃圾回收
切片的底层指向一个数组,该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候,底层的数组内层才会被释放,这种特性有时会致使程序占用多余的内存。
示例 函数 FindDigits
将一个文件加载到内存,而后搜索其中全部的数字并返回一个切片。
var digitRegexp = regexp.MustCompile("[0-9]+") func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) return digitRegexp.Find(b) }
这段代码能够顺利运行,但返回的 []byte
指向的底层是整个文件的数据。只要该返回的切片不被释放,垃圾回收器就不能释放整个文件所占用的内存。换句话说,一点点有用的数据却占用了整个文件的内存。
想要避免这个问题,能够经过拷贝咱们须要的部分到一个新的切片中:
func FindDigits(filename string) []byte { b, _ := ioutil.ReadFile(filename) b = digitRegexp.Find(b) c := make([]byte, len(b)) copy(c, b) return c }
2.数组和Map,数组的输出按照数组的放置顺序,可是Map是无序输出的,每次遍历的顺序不同;
var m = map[string]int{ "unix": 0, "python": 1, "go": 2, "javascript": 3, "testing": 4, "philosophy": 5, "startups": 6, "productivity": 7, "hn": 8, "reddit": 9, "C++": 10, } var keys []string for k := range m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println("Key:", k, "Value:", m[k]) }
3.go语言中的指针
指针是一种存储变量内存地址(Memory Address)的变量。
如上图所示,变量 b
的值为 156
,而 b
的内存地址为 0x1040a124
。变量 a
存储了 b
的地址。咱们就称 a
指向了 b
。
指针变量的类型为 *T
,该指针指向一个 T 类型的变量。
&b 获取b的内存地址值;*a 则是获取a值对应物理地址值的b值:156;
向函数传递指针参数
func change(val *int) { *val = 55 } func main() { a := 58 fmt.Println("value of a before function call is",a) b := &a change(b) fmt.Println("value of a after function call is", a) }
4.那么何时使用指针接收器,何时使用值接收器?
/* 使用值接收器的方法。 */ func (e Employee) changeName(newName string) { e.name = newName } /* 使用指针接收器的方法。 */ func (e *Employee) changeAge(newAge int) { e.age = newAge }
通常来讲,指针接收器可使用在:对方法内部的接收器所作的改变应该对调用者可见时。
指针接收器也能够被使用在以下场景:当拷贝一个结构体的代价过于昂贵时。考虑下一个结构体有不少的字段。在方法内使用这个结构体作为值接收器须要拷贝整个结构体,这是很昂贵的。在这种状况下使用指针接收器,结构体不会被拷贝,只会传递一个指针到方法内部使用。
在其余的全部状况,值接收器均可以被使用。
5.在方法中使用值接收器 与 在函数中使用值参数
这个话题不少Go语言新手都弄不明白。我会尽可能讲清楚。
当一个函数有一个值参数,它只能接受一个值参数。
当一个方法有一个值接收器,它能够接受值接收器和指针接收器。
让咱们经过一个例子来理解这一点。
package main import ( "fmt" ) type rectangle struct { length int width int } func area(r rectangle) { fmt.Printf("Area Function result: %d\n", (r.length * r.width)) } func (r rectangle) area() { fmt.Printf("Area Method result: %d\n", (r.length * r.width)) } func main() { r := rectangle{ length: 10, width: 5, } area(r) r.area() p := &r /* compilation error, cannot use p (type *rectangle) as type rectangle in argument to area */ //area(p) p.area()//经过指针调用值接收器 }
6.在方法中使用指针接收器 与 在函数中使用指针参数
和值参数相相似,函数使用指针参数只接受指针,而使用指针接收器的方法可使用值接收器和指针接收器。
package main import ( "fmt" ) type rectangle struct { length int width int } func perimeter(r *rectangle) { fmt.Println("perimeter function output:", 2*(r.length+r.width)) } func (r *rectangle) perimeter() { fmt.Println("perimeter method output:", 2*(r.length+r.width)) } func main() { r := rectangle{ length: 10, width: 5, } p := &r //pointer to r perimeter(p) p.perimeter() /* cannot use r (type rectangle) as type *rectangle in argument to perimeter */ //perimeter(r) r.perimeter()//使用值来调用指针接收器 }
7.并行和并发
并行不必定会加快运行速度,由于并行运行的组件之间可能须要相互通讯。在咱们浏览器的例子里,当文件下载完成后,应当对用户进行提醒,好比弹出一个窗口。因而,在负责下载的组件和负责渲染用户界面的组件之间,就产生了通讯。在并发系统上,这种通讯开销很小。但在多核的并行系统上,组件间的通讯开销就很高了。因此,并行不必定会加快运行速度!
8.线程与协程区别
线程切换从系统层面远不止 保存和恢复 CPU上下文这么简单。操做系统为了程序运行的高效性每一个线程都有本身缓存Cache等等数据,操做系统还会帮你作这些数据的恢复操做。因此线程的切换很是耗性能。可是协程的切换只是单纯的操做CPU的上下文,因此一秒钟切换个上百万次系统都抗的住。可是协程有一个问题,就是系统并不感知,因此操做系统不会帮你作切换。目前的协程框架通常都是设计成 1:N 模式。所谓 1:N 就是一个线程做为一个容器里面放置多个协程。那么谁来适时的切换这些协程?答案是有协程本身主动让出CPU,也就是每一个协程池里面有一个调度器,这个调度器是被动调度的。意思就是他不会主动调度。协程的切换很轻。
9.缓冲信道和非缓冲信道
缓冲的channel:保证往缓冲中存数据先于对应的取数据,简单说就是在取的时候里面确定有数据,不然就因取不到而阻塞。
非缓冲的channel:保证取数据先于存数据,就是保证存的时候确定有其余的goroutine在取,不然就因放不进去而阻塞
3、疑问点
go系列教程:https://studygolang.com/subject/2
信道接入技术及协议:https://blog.csdn.net/fivedoumi/article/details/52776832