0%

unsafe 是很神奇的,虽然它像普通的包那样并且像普通的包那样导入,但是事实上是由编译器实现的。它提供了对语言内置特性的访问功能,而这些特性一般是不可见的,因为他们暴露了 Go 详细的内存布局。把这些单独的函数放在一个包中,就使得它们的本来就不频繁的使用场合变得更加引入注目。包 unsafe 广泛使用在和操作系统交互的低级包(比如 runtime,os,syscall 和 net)中,但是普通程序从来不调用它。

阅读全文 »

之前使用过 sync.WaitGroup 来实现一对多的 goroutine 协作流程同步,sync.WaitGroup 只要保证计数周期的完整性就是可以复用的,sync.WaitGroup 使用的黄金规则可以总计为:统一 Add,并发 Done,然后 Wait。但是今天我们使用另一个工具来实现同步:context.Context

阅读全文 »

相比于Go宣扬的以独特的 “用通讯的方式共享数据”,通过共享数据的方式来传递信息和协调线程运行的做法其实更加主流,毕竟现代的大多数现代编程语言,都是用后一种并发编程的解决方案。一旦数据被多个线程共享,那么就很可能会产生争用和冲突的情况了,这种情况也被成为竟态条件,这往往会破坏数据的一致性。共享数据的一致性代表着:多个线程对共享数据的操作总能达到他们各自预期的效果

阅读全文 »

我强烈地意识到我余生很大一部分工作都用来寻找我程序中的bug

测试是自动化测试的简称,即编写简单的程序来确保程序(产品业务代码)在测试中针对特定输入产生期望的输出。这些测试要么是经过精心设计之后用来检测某种功能,要么是随机性的,用来扩大测试的覆盖面。Go 中的测试方法看上去相对比较低级,它依赖于命令 go test 和一些能用 go test 运行的测试函数编写约定。实际上,编写测试函数和编写原始程序没什么区别。

阅读全文 »

请猜一下下面这段代码的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func main() {
numbers2 := [...]int{1, 2, 3, 4, 5, 6}
maxIndex2 := len(numbers2) - 1
for i, e := range numbers2 {
if i == maxIndex2 {
numbers2[0] += e
} else {
numbers2[i+1] += e
}
}
fmt.Println(numbers2)
}
阅读全文 »

阅读全文 »

Don’t communicate by sharing memory; share memory by communicating.

并发编程表现为由若干个自主的的活动单元组成,他从来没有像今天这么重要。web服务器一次可以处理数千个请求。平板电脑和收集应用在渲染用户界面的同时,后端还同步进行着计算和处理网络请求。甚至传统的批处理任务—-读取数据,计算,将结果输出,也使用并发来隐藏I/O操作的延迟,充分利用现代的多核计算机,内核的个数每年变多,但是速度没什么变化。

Go有两种并发编程的风格,一种是goroutine通道,他们支持通信顺序进程(Communicating Sequential Process, CSP),CSP是一个并发的模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体,还有一种传统的共享内存多线程模型。

阅读全文 »

如果当函数调用发生错误时返回一个附加的结果作为错误值,习惯上将错误值作为最后一个结果返回。如果错误只有一种情况,结果通常设置为布尔类型,就像下面这个查询缓存的例子里面,往往都会成功,只有不存在对应的键值的时候返回错误:

1
2
3
4
value, ok := cache.lookup(key)
if !ok {
// ...cache[key] 不存在
}

更多的时候,尤其对于 I/O 操作,错误的原因可能多种多样,而调用者则需要一些详细的信息,在这种情况下,错误的结果是类型往往是 error

阅读全文 »