书籍简介

  • 名称:Go语言实战
  • 作者: 威廉·肯尼迪 (William Kennedy) / 布赖恩·克特森 (Brian Ketelsen) / 埃里克·圣马丁 (Erik St.Martin)
  • 内容:Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。本书向读者提供一个专注、全面且符合语言习惯的视角。Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。

第1章 关于Go语言的介绍

传统语言使用继承来扩展结构——Client 继承自 User, User 继承自 Entity, Go 语言与此不同,Go 开发者构建更小的类型——Customer 和 Admin,然后把这些小类型组合成更大的类型。

第4章 数组切片

如何计算长度和容量

函数 append 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25%的容量。随着语言的演化,这种增长算法可能会有所改变。

设置长度和容量一样的好处

内置函数 append 会首先使用可用容量。一旦没有可用容量,会分配一个新的底层数组。这导致很容易忘记切片间正在共享同一个底层数组。一旦发生这种情况,对切片

进行修改,很可能会导致随机且奇怪的问题。对切片内容的修改会影响多个切片,却很难找到问题的原因。

如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个 append 操作创建新的底层数组,与原有的底层数组分离。新切片与原有的底层数组分离后,可以安全地进行后续修改。

// 其长度和容量都是 5 个元素
source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
// 对第三个元素做切片,并限制容量
// 其长度和容量都是 1 个元素
slice := source[2 : 4: 4]
// 向 slice 追加新字符串
slice = append(slice, "Kiwi")
fmt.Println(source) // [Apple Orange Plum Banana Grape]
fmt.Println(slice) // [Plum Banana Kiwi]

range

range 创建了每个元素的副本,而不是直接返回对该元素的引用

// 创建一个整型切片
// 其长度和容量都是 4 个元素
slice := []int{10, 20, 30, 40}
// 迭代每个元素,并显示值和地址
for index, value := range slice {
fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X\n",
value, &value, &slice[index])
}

结果为

Value: 10 Value-Addr: C4200180A8 ElemAddr: C420014200
Value: 20 Value-Addr: C4200180A8 ElemAddr: C420014208
Value: 30 Value-Addr: C4200180A8 ElemAddr: C420014210
Value: 40 Value-Addr: C4200180A8 ElemAddr: C420014218

因为迭代返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以 value 的地址总

是相同的。要想获取每个元素的地址,可以使用切片变量和索引值。

在函数间传递切片

在 64 位架构的机器上,一个切片需要 24 字节的内存:指针字段需要 8字节,长度和容量字段分别需要 8字节。由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片复制到任意函数的时候,对底层数组大小都不会有影响。复制时只会复制切片本身,不会涉及底层数组。

第5章 Go语言的类型系统

接口

如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。

第6章 并发

并发与并行

并发( concurrency)不是并行( parallelism)。并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。在很多情况下,并发的效果比并行好,因为操作系统和硬件的总资源一般很少,但能支持系统同时做很多事情。这种“使用较少的资源做更多的事情” 的哲学,也是指导 Go 语言设计的哲学。

如果希望让 goroutine 并行,必须使用多于一个逻辑处理器。 当有多个逻辑处理器时,调度器会将 goroutine 平等分配到每个逻辑处理器上。这会让 goroutine 在不同的线程上运行。不过要想真的实现并行的效果,用户需要让自己的程序运行在有多个物理处理器的机器上。否则,哪怕 Go 语言运行时使用多个线程, goroutine 依然会在同一个物理处理器上并发运行,达不到并行的效果。

无缓冲的通道

示意图

无缓冲的通道示意图

代码示例

// 这个示例程序展示如何用无缓冲的通道来模拟
// 2 个 goroutine 间的网球比赛
package main import (
"fmt"
"math/rand"
"sync"
"time"
) // wg 用来等待程序结束
var wg sync.WaitGroup func init() {
rand.Seed(time.Now().UnixNano())
} // main 是所有 Go 程序的入口
func main() {
// 创建一个无缓冲的通道
court := make(chan int) // 计数加 2,表示要等待两个 goroutine
wg.Add(2) // 启动两个选手
go player("Nadal", court)
go player("Djokovic", court) // 发球
court <- 1 // 等待游戏结束
wg.Wait()
} // player 模拟一个选手在打网球
func player(name string, court chan int) {
// 在函数退出时调用 Done 来通知 main 函数工作已经完成
defer wg.Done() for {
// 等待球被击打过来
ball, ok := <-court
if !ok {
// 如果通道被关闭,我们就赢了
fmt.Printf("Player %s Won\n", name)
return
} // 选随机数,然后用这个数来判断我们是否丢球
n := rand.Intn(100)
if n % 13 == 0 {
fmt.Printf("Player %s Missed\n", name)
// 关闭通道,表示我们输了
close(court)
return
} // 显示击球数,并将击球数加 1
fmt.Printf("Player %s Hit %d\n", name, ball)
ball++ // 将球打向对手
court <- ball
}
}

有缓冲的通道

示意图

有缓冲的通道示意图

代码示例

// 这个示例程序展示如何使用
// 有缓冲的通道和固定数目的
// goroutine 来处理一堆工作
package main import (
"fmt"
"math/rand"
"sync"
"time"
) const (
numberGoroutines = 4 // 要使用的 goroutine 的数量
taskLoad = 10 // 要处理的工作的数量
) // wg 用来等待程序完成
var wg sync.WaitGroup // init 初始化包, Go 语言运行时会在其他代码执行之前
// 优先执行这个函数
func init() {
// 初始化随机数种子
rand.Seed(time.Now().Unix())
} // main 是所有 Go 程序的入口
func main() {
// 创建一个有缓冲的通道来管理工作
tasks := make(chan string, taskLoad) // 启动 goroutine 来处理工作
wg.Add(numberGoroutines)
for gr := 1; gr <= numberGoroutines; gr++ {
go worker(tasks, gr)
} // 增加一组要完成的工作
for post := 1; post <= taskLoad; post++ {
tasks <- fmt.Sprintf("Task : %d", post)
} // 当所有工作都处理完时关闭通道
// 以便所有 goroutine 退出
close(tasks) // 等待所有工作完成
wg.Wait()
} // worker 作为 goroutine 启动来处理
// 从有缓冲的通道传入的工作
func worker(tasks chan string, worker int) {
// 通知函数已经返回
defer wg.Done() for {
// 等待分配工作
task, ok := <-tasks
if !ok {
// 这意味着通道已经空了,并且已被关闭
fmt.Printf("Worker: %d : Shutting Down\n", worker)
return
} // 显示我们开始工作了
fmt.Printf("Worker: %d : Started %s\n", worker, task) // 随机等一段时间来模拟工作
sleep := rand.Int63n(100)
time.Sleep(time.Duration(sleep) * time.Millisecond) // 显示我们完成了工作
fmt.Printf("Worker: %d : Completed %s\n", worker, task)
}
}

当通道关闭后, goroutine 依旧可以从通道接收数据,但是不能再向通道里发送数据。能够从已经关闭的通道接收数据这一点非常重要,因为这允许通道关闭后依旧能取出其中缓冲的全部值,而不会有数据丢失。从一个已经关闭且没有数据的通道里获取数据,总会立刻返回,并返回一个通道类型的零值。如果在获取通道时还加入了可选的标志,就能得到通道的状态信息。

第8章 标准库

编码/解码

type gResult struct {
GsearchResultClass string `json:"GsearchResultClass"`
UnescapedURL string `json:"unescapedUrl"`
URL string `json:"url"`
VisibleURL string `json:"visibleUrl"`
CacheURL string `json:"cacheUrl"`
Title string `json:"title"`
TitleNoFormatting string `json:"titleNoFormatting"`
Content string `json:"content"`
}

你会注意到每个字段最后使用单引号声明了一个字符串。这些字符串被称作标签( tag),是提供每个字段的元信息的一种机制,将 JSON 文档和结构类型里的字段一一映射起来。如果不存在标签,编码和解码过程会试图以大小写无关的方式,直接使用字段的名字进行匹配。如果无法匹配,对应的结构类型里的字段就包含其零值。

《Go语言实战》书摘的更多相关文章

  1. Code Simplicity–The Science of Software Development 书摘

    Chapter1 Introduction That is the art and talent involved in programming—reducing complexity to simp ...

  2. 《CODE》书摘

    2016-11-08 14:59:16 可以说英语词汇就是一种编码. 2016-11-08 15:19:04 实际上任何两种不同的东西经过一定的组合都可以代表任何种类的信息. 2016-11-08 1 ...

  3. 《C Elements of Style》 书摘

    <C Elements of Style> 书摘 学完C语言和数据结构后,虽然能解决一些问题,但总觉得自己写的程序丑陋,不专业.这时候看到了Steve Oualline写的<C El ...

  4. Visual Studio Code 代理设置

    Visual Studio Code (简称 VS Code)是由微软研发的一款免费.开源的跨平台文本(代码)编辑器,在十多年的编程经历中,我使用过非常多的的代码编辑器(包括 IDE),例如 Fron ...

  5. 我们是怎么做Code Review的

    前几天看了<Code Review 程序员的寄望与哀伤>,想到我们团队开展Code Review也有2年了,结果还算比较满意,有些经验应该可以和大家一起分享.探讨.我们为什么要推行Code ...

  6. Code Review 程序员的寄望与哀伤

    一个程序员,他写完了代码,在测试环境通过了测试,然后他把它发布到了线上生产环境,但很快就发现在生产环境上出了问题,有潜在的 bug. 事后分析,是生产环境的一些微妙差异,使得这种 bug 场景在线下测 ...

  7. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM

    刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code Behind到MVC.MVP.MVVM>,是前一天晚上才定的,中午花了半小时准备了下 ...

  8. 在Visual Studio Code中配置GO开发环境

    一.GO语言安装 详情查看:GO语言下载.安装.配置 二.GoLang插件介绍 对于Visual Studio Code开发工具,有一款优秀的GoLang插件,它的主页为:https://github ...

  9. 代码的坏味道(14)——重复代码(Duplicate Code)

    坏味道--重复代码(Duplicate Code) 重复代码堪称为代码坏味道之首.消除重复代码总是有利无害的. 特征 两个代码片段看上去几乎一样. 问题原因 重复代码通常发生在多个程序员同时在同一程序 ...

  10. http status code

    属于转载 http status code:200:成功,服务器已成功处理了请求,通常这表示服务器提供了请求的网页 404:未找到,服务器未找到 201-206都表示服务器成功处理了请求的状态代码,说 ...

随机推荐

  1. (一)selenium发展史(专治selenium小白)

    Jason Huggins在2004年发起了Selenium项目,当时身处ThoughtWorks的他,为了不想让自己的时间浪费在无聊的重复性工作中,幸运的是,所有被测试的浏览器都支持Javascri ...

  2. ORACLE_DELETE

    SQL DELETE Statement The SQL DELETE Statement The DELETE statement is used to delete existing record ...

  3. June 07th 2017 Week 23rd Wednesday

    Failure is the condiment that gives success its flavor. 失败是让成功变美味的调味料. There are kinds of flavors in ...

  4. 同时开左右两个SAPGUI编辑器显示同一段ABAP代码

    很多文本编辑器都支持同时开左右两个窗口显示同一段代码,使用场景可能是比较同一段代码的不同版本差异,或者是ABAP里,同一段代码在Netweaver不同版本里的实现差异,比如版本为SP1的系统A和版本为 ...

  5. DOS下启动MySQL时输入net start mysql 提示服务名无效的问题

    原因:mysql服务名错误. 正确做法:net start  +mysql服务名

  6. 页面三个txt加载联动省市县的代码,类似淘宝的收货地址的布局

    页面三个txt加载联动省市县的代码,假如有一个树形的JSON,分别显示的省市县这时候三个TXT怎么做联动效果呢,这里用framework7为例HTML: <div class="lis ...

  7. HDU 4165 卡特兰

    题意:有n个药片,每次吃半片,吃2n天,那么有多少种吃法. 分析:如果说吃半片,那么一定要吃过一整片,用 ) 表示吃半片,用 ( 表示吃整片,那么就是求一个正确的括号匹配方案数,即卡特兰数. 卡特兰数 ...

  8. 【转】Android Fragment 真正的完全解析(上)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37970961 自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fra ...

  9. Hangfire 在asp.net core环境的使用

    hf被定义为分布式后台服务,更加类似job作业的服务做作业的插件有quartz.net,JobScheduler 等当然,都有一些分别和适用的场景.1.安装需要安装Hangfire.CoreHangf ...

  10. 【luogu P2590 [ZJOI2008]树的统计】 题解

    题目链接:https://www.luogu.org/problemnew/show/P2590 我想学树剖QAQ #include <cstdio> #include <cstri ...