Go语言学习笔记-1

第一章 关于Go语言

1.1 背景

1.2 Go语言特性

1.2.1 开发速度

1.2.2 并发

  Go 语言对并发的支持是这门语言最重要的特性之一。goroutine 很像线程,但是它占用的 内存远少于线程,使用它需要的代码更少。通道(channel)是一种内置的数据结构,可以让 用户在不同的 goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 goroutine 之间发送消息,而不是让多个 goroutine 争夺同一个数据的使用权。让我们看看这些特性的细节。

1.2.2.1 goroutine

  goroutine 是可以与其他 goroutine 并行执行的函数,同时也会与主程序(程序的入口)并行 执行。在其他编程语言中,你需要用线程来完成同样的事情,而在 Go 语言中会使用同一个线程 来执行多个 goroutine。例如,用户在写一个 Web 服务器,希望同时处理不同的 Web 请求,如果 使用 C 或者 Java,不得不写大量的额外代码来使用线程。在 Go 语言中,net/http 库直接使用了 内置的 goroutine。每个接收到的请求都自动在其自己的 goroutine 里处理。goroutine 使用的内存 比线程更少,Go 语言运行时会自动在配置的一组逻辑处理器上调度执行 goroutine。每个逻辑处理器绑定到一个操作系统线程上(见图 1-2)。这让用户的应用程序执行效率更高,而开发工作量显著减少。
如果想在执行一段代码的同时,并行去做另外一些事情,goroutine 是很好的选择。下面是一 个简单的例子:

func log(msg string) {  ...这里是一些记录日志的代码 
} // 代码里有些地方检测到了错误 
go log("发生了可怕的事情")

Go语言学习笔记-1_第1张图片
  关键字 go 是唯一需要去编写的代码,调度 log 函数作为独立的 goroutine 去运行,以便与其他 goroutine 并行执行。这意味着应用程序的其余部分会与记录日志并行执行,通常这种并行 能让最终用户觉得性能更好。就像之前说的,goroutine 占用的资源更少,所以常常能启动成千上万个 goroutine。

1.2.2.2通道

  通道是一种数据结构,可以让 goroutine 之间进行安全的数据通信。通道可以帮用户避免其他语言里常见的共享内存访问的问题。
  并发的最难的部分就是要确保其他并发运行的进程、线程或 goroutine 不会意外修改用户的 数据。当不同的线程在没有同步保护的情况下修改同一个数据时,总会发生灾难。在其他语言中,如果使用全局变量或者共享内存,必须使用复杂的锁规则来防止对同一个变量的不同步修改。
  为了解决这个问题,通道提供了一种新模式,从而保证并发修改时的数据安全。通道这一模 式保证同一时刻只会有一个 goroutine 修改数据。通道用于在几个运行的 goroutine 之间发送数据。 在图中可以看到数据是如何流动的示例。想象一个应用程序,有多个进程需要顺序读取或者修改某个数据,使用 goroutine 和通道,可以为这个过程建立安全的模型。

Go语言学习笔记-1_第2张图片
  图中有 3 个 goroutine,还有 2 个不带缓存的通道。第一个 goroutine 通过通道把数 据传给已经在等待的第二个 goroutine。在两个 goroutine 间传输数据是同步的,一旦传输完成,两个 goroutine 都会知道数据已经完成传输。当第二个 goroutine 利用这个数据完成其任务后,将这个数据传给第三个正在等待的 goroutine。这次传输依旧是同步的,两个 goroutine 都会确认数据传输完成。这种在 goroutine 之间安全传输数据的方法不需要任何锁或者同步机制。

  需要强调的是,通道并不提供跨 goroutine 的数据访问保护机制。如果通过通道传输数据的 一份副本,那么每个 goroutine 都持有一份副本,各自对自己的副本做修改是安全的。当传输的 是指向数据的指针时,如果读和写是由不同的 goroutine 完成的,每个 goroutine 依旧需要额外的同步动作。

1.2.3 Go 语言的类型系统

  Go 语言提供了灵活的、无继承的类型系统,无需降低运行性能就能最大程度上复用代码。这个类型系统依然支持面向对象开发,但避免了传统面向对象的问题。如果你曾经在复杂的 Java 和C++程序上花数周时间考虑如何抽象类和接口,你就能意识到Go语言的类型系统有多么简单。 Go 开发者使用组合(composition)设计模式,只需简单地将一个类型嵌入到另一个类型,就能复用所有的功能。其他语言也能使用组合,但是不得不和继承绑在一起使用,结果使整个用法非常复杂,很难使用。在 Go 语言中,一个类型由其他更微小的类型组合而成,避免了传统的基于继承的模型。

  另外,Go 语言还具有独特的接口实现机制,允许用户对行为进行建模,而不是对类型进行建模。在 Go 语言中,不需要声明某个类型实现了某个接口,编译器会判断一个类型的实例是否符合正在使用的接口。Go 标准库里的很多接口都非常简单,只开放几个函数。从实践上讲,尤其对那些使用类似 Java 的面向对象语言的人来说,需要一些时间才能习惯这个特性。

1.2.3.1 类型简单

  Go 语言不仅有类似int和string这样的内置类型,还支持用户定义的类型。在 Go 语言中,用户定义的类型通常包含一组带类型的字段,用于存储数据。Go 语言的用户定义的类型看 起来和 C 语言的结构很像,用起来也很相似。不过 Go 语言的类型可以声明操作该类型数据的方法。传统语言使用继承来扩展结构——Client 继承自User,User 继承自Entity,Go 语言与此不同, Go 开发者构建更小的类型——Customer 和 Admin,然后把这些小类型组合成更大的类型。

Go语言学习笔记-1_第3张图片

1.2.3.2 Go 接口对一组行为建模

  接口用于描述类型的行为。如果一个类型的实例实现了一个接口,意味着这个实例可以执行一组特定的行为。你甚至不需要去声明这个实例实现某个接口,只需要实现这组行为就好。其他的语言把这个特性叫作鸭子类型——如果它叫起来像鸭子,那它就可能是只鸭子。Go 语言的接 口也是这么做的。在 Go 语言中,如果一个类型实现了一个接口的所有方法,那么这个类型的实例就可以存储在这个接口类型的实例中,不需要额外声明。

1.2.4 内存管理

  不当的内存管理会导致程序崩溃或者内存泄漏,甚至让整个操作系统崩溃。Go 语言拥有现代化的垃圾回收机制,能帮你解决这个难题。在其他系统语言(如 C 或者 C++)中,使用内存前要先分配这段内存,而且使用完毕后要将其释放掉。哪怕只做错了一件事,都可能导致程序崩溃或者内存泄漏。可惜,追踪内存是否还被使用本身就是十分艰难的事情,而要想支持多线程和高并发,更是让这件事难上加难。虽然 Go 语言的垃圾回收会有一些额外的开销,但是编程时, 能显著降低开发难度。Go 语言把无趣的内存管理交给专业的编译器去做,而让程序员专注于更有趣的事情。

1.3 经典代码HelloWorld

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello,World!")
}

你可能感兴趣的:(Go)