Go 的优势:
1 开发速度
Go 语言 使用了更加智能的编译器,并简化了解决依赖的算法,最终提供了 更快的编译速度。
编译Go 程序时,编译器只会关注那些直接被引用的库,
而Java、C 和C++那样,要遍历依赖链中所有依赖的库
因此,很多Go 程序可以在1 秒内编译完。在现代硬件上,编译整个Go语言的源码树只需要20 秒。
动态语言编写应用程序: 没有从编译代码到执行代码的中间过程,用 可以快速看到输出。
代价是: 动态语言 不提供静态语言提供的类型安全特性,不得不经常用大量的测试套件来避免在
运行的时候出现类型错误这类bug。
在Go 语言里,完全不用为这件事情操心,因为编译器就能帮用户捕获这种类型错误。
2 并发
现代计算机都拥有多个核,但是大部分编程语言都没有有效的工具让程序可以轻易利用这些资源。这些语言需要写 大量的线程同步代码来利用多个核,很容易导致错误。
Go 语言对并发的支持
是这门语言最重要的特性之一。
goroutine
很像线程,但是它占用的内存远少于线程,使用它需要的代码更少。
通道(channel)
是一种 内置的数据结构,可以让用户 在不同的goroutine 之间同步发送具有类型的消息。
这让编程模型更倾向于在goroutine之间发送消息,而不是让多个goroutine 争夺同一个数据的使用权。
(1) goroutine
goroutine
是可以与其他goroutine 并行执行的函数,同时也会与主程序(程序的入口)并行执行。
在其他编程语言中,你需要用线程
来完成同样的事情,而在Go 语言中会使用同一个线程来执行多个goroutine。
例如,用户在写一个Web 服务器,希望同时处理不同的Web 请求,如果使用C 或者Java,不得不写大量的额外代码来使用线程。
在Go 语言中,net/http 库直接使用了内置的goroutine。每个接收到的请求都自动在其自己的goroutine 里处理。goroutine 使用的内存
比线程更少,Go 语言运行时会自动在配置的一组逻辑处理器上调度执行goroutine。
如果想在执行一段代码的同时,并行去做另外一些事情,goroutine 是很好的选择。goroutine 占用的资源更少,所以常常能启动成千上 万个goroutine。
(2) 通道
通道
是一种 数据结构
,可以让goroutine 之间进行安全的数据通信。通道可以帮用户避免其他语言里常见的共享内存访问的问题
。
并发
的最难的部分就是要确保其他并发运行的进程、线程或goroutine 不会意外修改用户的
数据。当不同的线程在没有同步保护的情况下修改同一个数据时,总会发生灾难。在其他语言中,
如果使用全局变量或者共享内存
,必须使用复杂的锁规则
来防止对同一个变量的不同步修改。
通道
这一模式保证同一时刻只会有一个goroutine 修改数据。通道用于在几个运行的goroutine 之间发送数据。
这种在goroutine 之间安全传输数据的方法不需要任何锁或者同步机制。
需要强调的是,通道并不提供跨goroutine 的数据访问保护机制。如果通过通道传输数据的一份副本,
那么每个goroutine 都持有一份副本,各自对自己的副本做修改是安全的。当传输的是指向数据的指针时,
如果读和写是由不同的goroutine 完成的,每个goroutine 依旧需要额外的同步动作。
3 Go 语言的类型系统
Go 语言提供了灵活的、无继承的类型系统,无需降低运行性能就能最大程度上复用代码。
这个类型系统依然支持面向对象开发,但避免了传统面向对象的问题。
Go 开发者使用组合(composition)设计模式
,只需简单地 将一个类型嵌入到另一个类型,就能
复用所有的功能。
Go 语言还具有 独特的接口实现机制
,允许用户对行为进行建模,而不是对类型进行建模。
在Go 语言中,不需要声明某个类型实现了某个接口,编译器会判断一个类型的实例是否 符合正在使用的接口。
(1) 类型简单
Go 语言不仅有类似int 和string 这样的内置类型,还支持用户定义的类型。
在Go 语言中,用户定义的类型通常包含一组带类型的字段,用于存储数据。
Go 语言的用户定义的类型看起来和C 语言的结构很像,用起来也很相似。
(2) Go 接口对一组行为建模
接口
用于描述类型的行为。
如果一个类型的实例实现了一个接口,意味着这个实例可以执行一组特定的行为。
你甚至不需要去声明这个实例实现某个接口,只需要实现这组行为
就好。
其他的语言把这个特性叫作鸭子类型
——如果它叫起来像鸭子,那它就可能是只鸭子。
在Go 语言中,如果一个类型实现了一个接口的所有方法,那么这个类型的实例就可以存储在这个接口类型的实例中,不需要额外声明。
Go 语言的接口一般只会描述一个单一的动作
。
在Go 语言中,最常使用的接口之一是io.Reader。这个接口提供了一个简单的方法,用来声明一个类型有数据可以读取。
标准库内的其他函数都能理解这个接口。这个接口的定义如下:
type Reader interface {
Read(p []byte) (n int, err error)
}
为了实现io.Reader 这个接口,你只需要实现一个Read 方法,这个方法接受一个byte切片,返回一个整数和可能出现的错误。
这和传统的面向对象编程语言的接口系统
有本质的区别。
Go 语言的接口更小,只倾向于定义一个单一的动作。
实际使用中,这更有利于使用组合来复用代码。
用户几乎可以给所有包含数据的类型实现io.Reader 接口,然后把这个类型的实例传给任意一个知道如何读取
o.Reader 的Go 函数。
Go 语言的整个网络库都使用了io.Reader 接口,这样可以将程序的功能和不同网络的实现分离。这样的接口用起来有趣、优雅且自由。文件、缓冲区、套接字以及其他的数据源
都实现了io.Reader 接口。使用同一个接口,可以高效地操作数据,而不用考虑到底数据
来自哪里。
4 内存管理
不当的内存管理会导致程序崩溃或者内存泄漏,甚至让整个操作系统崩溃。
Go 语言拥有现代化的垃圾回收机制
在其他系统语言(如C 或者C++)中,使用内存前要先分配这段内存,而且使用完毕后要将其释放掉。
哪怕只做错了一件事,都可能导致程序崩溃或者内存泄漏。
可惜,追踪内存是否还被使用本身就是十分艰难的事情,而要想支持多线程和高并发,更是让这件事难上加难。
虽然Go 语言的垃圾回收会有一些额外的开销,但是编程时,能显著降低开发难度。Go 语言把无趣的内存管理交给专业的编译器去做,而让程序员专注于更
有趣的事情。