24 | 测试的基本规则和流程(下) Go 语言是一门很重视程序测试的编程语言,所以在上一篇中,我与你再三强调了程序测试的重要性,同时,也介绍了关于go test命令的基本规则和主要流程的内容.今天我们继续分享测试的基本规则和流程. 知识扩展 问题 1:怎样解释功能测试的测试结果? demo53.go package main import ( "errors" "flag" "fmt" ) var name string func init()…
03 | 库源码文件 在我的定义中,库源码文件是不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用(只要遵从 Go 语言规范的话). 这里的"其他代码"可以与被使用的程序实体在同一个源码文件内,也可以在其他源码文件,甚至其他代码包中. 那么程序实体是什么呢?在 Go 语言中,程序实体是变量.常量.函数.结构体和接口的统称.我们总是会先声明(或者说定义)程序实体,然后再去使用. 比如在上一篇的例子中,我们先定义了变量name,然后在main函数中调用fmt.P…
23 | 测试的基本规则和流程 (上) 在接下来的日子里,我将带你去学习在 Go 语言编程进阶的道路上,必须掌握的附加知识,比如:Go 程序测试.程序监测,以及 Go 语言标准库中各种常用代码包的正确用法. 从上个世纪到今日今时,程序员们,尤其是国内的程序员们,都对编写程序乐此不疲,甚至废寝忘食(比如我自己就是一个例子). 因为这是我们普通人训练自我.改变生活.甚至改变世界的一种特有的途径.不过,同样是程序,我们却往往对编写用于测试的程序敬而远之.这是为什么呢? 我个人感觉,从人的本性来讲,我们…
25 | 更多的测试手法 在本篇文章,我会继续为你讲解更多更高级的测试方法.这会涉及testing包中更多的 API.go test命令支持的,更多标记更加复杂的测试结果,以及测试覆盖度分析等等. 前导内容:-cpu 的功能 续接前文.我在前面提到了go test命令的标记-cpu,它是用来设置测试执行最大 P 数量的列表的. 复习一下,我在讲 go 语句的时候说过,这里的 P 是 processor 的缩写,每个 processor 都是一个可以承载若干个 G,且能够使这些 G 适时地与 M…
26 | sync.Mutex与sync.RWMutex 从本篇文章开始,我们将一起探讨 Go 语言自带标准库中一些比较核心的代码包.这会涉及这些代码包的标准用法.使用禁忌.背后原理以及周边的知识. 既然 Go 语言是以独特的并发编程模型傲视群雄的语言,那么我们就先来学习与并发编程关系最紧密的代码包. 前导内容: 竞态条件.临界区与同步工具 我们首先要看的就是sync包.这里的"sync"的中文意思是"同步".我们下面就从同步讲起. 相比于 Go 语言宣扬的&quo…
34 | 并发安全字典sync.Map (上) 我们今天再来讲一个并发安全的高级数据结构:sync.Map.众所周知,Go 语言自带的字典类型map并不是并发安全的. 前导知识:并发安全字典诞生史 换句话说,在同一时间段内,让不同 goroutine 中的代码,对同一个字典进行读写操作是不安全的.字典值本身可能会因这些操作而产生混乱,相关的程序也可能会因此发生不可预知的问题. 在sync.Map出现之前,我们如果要实现并发安全的字典,就只能自行构建.不过,这其实也不是什么麻烦事,使用 sync.…
36 | unicode与字符编码 在开始今天的内容之前,我先来做一个简单的总结. Go 语言经典知识总结 在数据类型方面有: 基于底层数组的切片: 用来传递数据的通道: 作为一等类型的函数: 可实现面向对象的结构体: 能无侵入实现的接口等. 在语法方面有: 异步编程神器go语句: 函数的最后关卡defer语句: 可做类型判断的switch语句: 多通道操作利器select语句: 非常有特色的异常处理函数panic和recover. 除了这些,我们还一起讨论了测试 Go 程序的主要方式.这涉及了…
40 | io包中的接口和工具 (上) 我们在前几篇文章中,主要讨论了strings.Builder.strings.Reader和bytes.Buffer这三个数据类型. 知识回顾 还记得吗?当时我还问过你"它们都实现了哪些接口".在我们继续讲解io包中的接口和工具之前,我先来解答一下这个问题. strings.Builder类型主要用于构建字符串,它的指针类型实现的接口有io.Writer.io.ByteWriter和fmt.Stringer.另外,它其实还实现了一个io包的包级私…
44 | 使用os包中的API (上) 我们今天要讲的是os代码包中的 API.这个代码包可以让我们拥有操控计算机操作系统的能力. 前导内容:os 包中的 API 这个代码包提供的都是平台不相关的 API.那么说,什么叫平台不相关的 API 呢? 它的意思是:这些 API 基于(或者说抽象自)操作系统,为我们使用操作系统的功能提供高层次的支持,但是,它们并不依赖于具体的操作系统. 不论是 Linux.macOS.Windows,还是 FreeBSD.OpenBSD.Plan9,os代码包都可以为…
46 | 访问网络服务 前导内容:socket 与 IPC 人们常常会使用 Go 语言去编写网络程序(当然了,这方面也是 Go 语言最为擅长的事情).说到网络编程,我们就不得不提及 socket. socket,常被翻译为套接字,它应该算是网络编程世界中最为核心的知识之一了.关于 socket,我们可以讨论的东西太多了,因此,我在这里只围绕着 Go 语言向你介绍一些关于它的基础知识. 所谓 socket,是一种 IPC 方法.IPC 是 Inter-Process Communication 的…
29 | 原子操作(上) 我们在前两篇文章中讨论了互斥锁.读写锁以及基于它们的条件变量,先来总结一下. 互斥锁是一个很有用的同步工具,它可以保证每一时刻进入临界区的 goroutine 只有一个.读写锁对共享资源的写操作和读操作则区别看待,并消除了读操作之间的互斥. 条件变量主要是用于协调想要访问共享资源的那些线程.当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程,它既可以基于互斥锁,也可以基于读写锁.当然了,读写锁也是一种互斥锁,前者是对后者的扩展. 通过对互斥锁的合理使用,我们…
30 | 原子操作(下) 我们接着上一篇文章的内容继续聊,上一篇我们提到了,sync/atomic包中的函数可以做的原子操作有:加法(add).比较并交换(compare and swap,简称 CAS).加载(load).存储(store)和交换(swap).并且以此衍生出了两个问题. 今天我们继续来看第三个衍生问题: 比较并交换操作与交换操作相比有什么不同?优势在哪里? 回答是:比较并交换操作即 CAS 操作,是有条件的交换操作,只有在条件满足的情况下才会进行值的交换. 所谓的交换指的是,把…
31 | sync.WaitGroup和sync.Once 我们在前几次讲的互斥锁.条件变量和原子操作都是最基本重要的同步工具.在 Go 语言中,除了通道之外,它们也算是最为常用的并发安全工具了. 说到通道,不知道你想过没有,之前在一些场合下里,我们使用通道的方式看起来都似乎有些蹩脚. 比如:声明一个通道,使它的容量与我们手动启用的 goroutine 的数量相同,之后再利用这个通道,让主 goroutine 等待其他 goroutine 的运行结束. 这一步更具体地说就是:让其他的 gorou…
33 | 临时对象池sync.Pool 到目前为止,我们已经一起学习了 Go 语言标准库中最重要的那几个同步工具,这包括非常经典的互斥锁.读写锁.条件变量和原子操作,以及 Go 语言特有的几个同步工具: 1.sync/atomic.Value 2.sync.Once 3.sync.WaitGroup 4.context.Context 今天,我们来讲 Go 语言标准库中的另一个同步工具:sync.Pool. sync.Pool类型可以被称为临时对象池,它的值可以被用来存储临时的对象.与 Go 语…
37 | strings包与字符串操作 Go 语言不但拥有可以独立代表 Unicode 字符的类型rune,而且还有可以对字符串值进行 Unicode 字符拆分的for语句. 除此之外,标准库中的unicode包及其子包还提供了很多的函数和数据类型,可以帮助我们解析各种内容中的 Unicode 字符. 这些程序实体都很好用,也都很简单明了,而且有效地隐藏了 Unicode 编码规范中的一些复杂的细节.我就不在这里对它们进行专门的讲解了. 我们今天主要来说一说标准库中的strings代码包.这个代…
48 | 程序性能分析基础(上) 作为拾遗的部分,今天我们来讲讲与 Go 程序性能分析有关的基础知识. Go 语言为程序开发者们提供了丰富的性能分析 API,和非常好用的标准工具.这些 API 主要存在于: 1.runtime/pprof: 2.net/http/pprof: 3.runtime/trace: 这三个代码包中. 另外,runtime代码包中还包含了一些更底层的 API.它们可以被用来收集或输出 Go 程序运行过程中的一些关键指标,并帮助我们生成相应的概要文件以供后续分析时使用.…
49 | 程序性能分析基础(下) 在上一篇文章中,我们围绕着"怎样让程序对 CPU 概要信息进行采样"这一问题进行了探讨,今天,我们再来一起看看它的拓展问题. 知识扩展 问题 1:怎样设定内存概要信息的采样频率? 针对内存概要信息的采样会按照一定比例收集 Go 程序在运行期间的堆内存使用情况.设定内存概要信息采样频率的方法很简单,只要为runtime.MemProfileRate变量赋值即可. 这个变量的含义是,平均每分配多少个字节,就对堆内存的使用情况进行一次采样.如果把该变量的值设…
27 | 条件变量sync.Cond (上) 前导内容:条件变量与互斥锁 我们常常会把条件变量这个同步工具拿来与互斥锁一起讨论.实际上,条件变量是基于互斥锁的,它必须有互斥锁的支撑才能发挥作用. 条件变量并不是被用来保护临界区和共享资源的,它是用于协调想要访问共享资源的那些线程的.当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程. 比如说,我们两个人在共同执行一项秘密任务,这需要在不直接联系和见面的前提下进行.我需要向一个信箱里放置情报,你需要从这个信箱中获取情报.这个信箱就相当于…
28 | 条件变量sync.Cond (下) 问题 1:条件变量的Wait方法做了什么? 在了解了条件变量的使用方式之后,你可能会有这么几个疑问. 1.为什么先要锁定条件变量基于的互斥锁,才能调用它的Wait方法? 2.为什么要用for语句来包裹调用其Wait方法的表达式,用if语句不行吗? 这些问题我在面试的时候也经常问.你需要对这个Wait方法的内部机制有所了解才能回答上来. 条件变量的Wait方法主要做了四件事. 1.把调用它的 goroutine(也就是当前的 goroutine)加入到…
35 | 并发安全字典sync.Map (下) 我们在上一篇文章中谈到了,由于并发安全字典提供的方法涉及的键和值的类型都是interface{},所以我们在调用这些方法的时候,往往还需要对键和值的实际类型进行检查. 这里大致有两个方案.我们上一篇文章中提到了第一种方案,在编码时就完全确定键和值的类型,然后利用 Go 语言的编译器帮我们做检查. 这样做很方便,不是吗?不过,虽然方便,但是却让这样的字典类型缺少了一些灵活性. 如果我们还需要一个键类型为uint32并发安全字典的话,那就不得不再如法炮…
41 | io包中的接口和工具 (下) 上一篇文章中,我主要讲到了io.Reader的扩展接口和实现类型.当然,io代码包中的核心接口不止io.Reader一个. 我们基于它引出的一条主线,只是io包类型体系中的一部分.我们很有必要再从另一个角度去探索一下,以求对io包有更加全面的了解. 下面的一个问题就与此有关. 知识扩展问题: io包中的接口都有哪些?它们之间都有着怎样的关系? 我们可以把没有嵌入其他接口并且只定义了一个方法的接口叫做简单接口.在io包中,这样的接口一共有 11 个. 在它们…
38 | bytes包与字节串操作(上) 前导内容: bytes.Buffer基础知识 strings包和bytes包可以说是一对孪生兄弟,它们在 API 方面非常的相似.单从它们提供的函数的数量和功能上讲,差别可以说是微乎其微. 只不过,strings包主要面向的是 Unicode 字符和经过 UTF-8 编码的字符串,而bytes包面对的则主要是字节和字节切片. 我今天会主要讲bytes包中最有特色的类型Buffer.顾名思义,bytes.Buffer类型的用途主要是作为字节序列的缓冲区.…
43 | bufio包中的数据类型(下) 在上一篇文章中,我提到了bufio包中的数据类型主要有Reader.Scanner.Writer和ReadWriter.并着重讲到了bufio.Reader类型与bufio.Writer类型,今天,我们继续专注bufio.Reader的内容来进行学习. 知识扩展 问题 :bufio.Reader类型读取方法有哪些不同? bufio.Reader类型拥有很多用于读取数据的指针方法,这里面有 4 个方法可以作为不同读取流程的代表,它们是:Peek.Read.…
47 | 基于HTTP协议的网络服务 我们在上一篇文章中简单地讨论了网络编程和 socket,并由此提及了 Go 语言标准库中的syscall代码包和net代码包. 我还重点讲述了net.Dial函数和syscall.Socket函数的参数含义.前者间接地调用了后者,所以正确理解后者,会对用好前者有很大裨益. 之后,我们把视线转移到了net.DialTimeout函数以及它对操作超时的处理上,这又涉及了net.Dialer类型.实际上,这个类型正是net包中这两个"拨号"函数的底层实现…
39 | bytes包与字节串操作(下) 在上一篇文章中,我们分享了bytes.Buffer中已读计数的大致功用,并围绕着这个问题做了解析,下面我们来进行相关的知识扩展. 知识扩展 问题 1:bytes.Buffer的扩容策略是怎样的? Buffer值既可以被手动扩容,也可以进行自动扩容.并且,这两种扩容方式的策略是基本一致的.所以,除非我们完全确定后续内容所需的字节数,否则让Buffer值自动去扩容就好了. 在扩容的时候,Buffer值中相应的代码(以下简称扩容代码)会先判断内容容器的剩余容量…
42 | bufio包中的数据类型 (上) 今天,我们来讲另一个与 I/O 操作强相关的代码包bufio.bufio是"buffered I/O"的缩写.顾名思义,这个代码包中的程序实体实现的 I/O 操作都内置了缓冲区. bufio包中的数据类型主要有: 1.Reader: 2.Scanner: 3.Writer和ReadWriter. 与io包中的数据类型类似,这些类型的值也都需要在初始化的时候,包装一个或多个简单 I/O 接口类型的值.(这里的简单 I/O 接口类型指的就是io包…
45 | 使用os包中的API (下) 我们在上一篇文章中.从"os.File类型都实现了哪些io包中的接口"这一问题出发,介绍了一系列的相关内容.今天我们继续围绕这一知识点进行扩展. 知识扩展 问题 1:可应用于File值的操作模式都有哪些? 针对File值的操作模式主要有只读模式.只写模式和读写模式. 这些模式分别由常量os.O_RDONLY.os.O_WRONLY和os.O_RDWR代表.在我们新建或打开一个文件的时候,必须把这三个模式中的一个设定为此文件的操作模式. 除此之外,…
目录 开篇词 | 跟着学,你也能成为Go语言高手 导读 | 写给0基础入门的Go语言学习者 导读 | 学习专栏的正确姿势 开篇词 | 跟着学,你也能成为Go语言高手 Go 语言是由 Google 出品的一门通用型计算机编程语言. Go 程序可以在装有 Windows.Linux.FreeBSD 等操作系统的服务器上运行,并用于提供基础软件支撑.API 服务.Web 服务.网页服务等等. Go 语言也在移动端进行了积极的探索,现在在 Android 和 iOS 上都可以运行其程序.另外,Go 语言…
14 | 接口类型的合理运用 前导内容:正确使用接口的基础知识 在 Go 语言的语境中,当我们在谈论"接口"的时候,一定指的是接口类型.因为接口类型与其他数据类型不同,它是没法被实例化的. 更具体地说,我们既不能通过调用new函数或make函数创建出一个接口类型的值,也无法用字面量来表示一个接口类型的值. 对于某一个接口类型来说,如果没有任何数据类型可以作为它的实现,那么该接口的值就不可能存在. 我已经在前面展示过,通过关键字type和interface,我们可以声明出接口类型. 接口…
22 | panic函数.recover函数以及defer语句(下) 我在前一篇文章提到过这样一个说法,panic 之中可以包含一个值,用于简要解释引发此 panic 的原因. 如果一个 panic 是我们在无意间引发的,那么其中的值只能由 Go 语言运行时系统给定.但是,当我们使用panic函数有意地引发一个 panic 的时候,却可以自行指定其包含的值.我们今天的第一个问题就是针对后一种情况提出的. 知识扩展 问题 1:怎样让 panic 包含一个值,以及应该让它包含什么样的值? 这其实很简…